Package: tftest
Python Test Helper for TerraformThis simple helper facilitates testing Terraform modules from Python unit tests, by wrapping the Terraform executable and exposing convenience methods to set up fixtures, execute Terraform commands, and parse their output.It allows for different types of tests: lightweight tests that only use Terraforminitandplanto ensure code is syntactically correct and the right number and type of resources should be created, or full-fledged tests that run the fullapply/output/destroycycle, and can then be used to test the actual created resources, or the state file.As an additional convenience, the module also provides an easy way to request and access the plan output (viaterraform plan -outandterraform show) and the outputs (viaterraform output -json), and return them wrapped in simple classes that streamline accessing their attributes.This module is heavily inspired by two projects:Terratestfor the lightweight approach to testing Terraform, andpython-terraformfor wrapping the Terraform command in Python.Example UsageThetestfolder contains simple examples on how to write tests for bothplanandapply, using either synthetic fixtures (simple representations of the plan output and output files), or minimal root modules. More examples can be found in theCloud Foundation Fabricrepository, for which this module was developed.This is a test that uses plan output on an actual module:importpytestimporttftest@pytest.fixturedefplan(fixtures_dir):tf=tftest.TerraformTest('plan',fixtures_dir)tf.setup(extra_files=['plan.auto.tfvars'])returntf.plan(output=True)deftest_variables(plan):assert'prefix'inplan.variablesassertplan.variables['names']==['one','two']deftest_outputs(plan):assertsorted(plan.outputs['gcs_buckets'].keys())==plan.variables['names']deftest_root_resource(plan):res=plan.resources['google_project_iam_member.test_root_resource']assertres['values']['project']==plan.variables['project_id']deftest_modules(plan):mod=plan.modules['module.gcs-buckets']res=mod.resources['google_storage_bucket.buckets[0]']assertres['values']['location']==plan.variables['gcs_location']Terragrunt supportSupport for Terragrunt actually follows the same principle of the thinTerraformTestwrapper.Please see the following example for how to use it:importpytestimporttftest@pytest.fixturedefrun_all_apply_out(fixtures_dir):# notice for run-all, you need to specify when TerragruntTest is constructedtg=tftest.TerragruntTest('tg_apply_all',fixtures_dir,tg_run_all=True)# the rest is very similar to how you use TerraformTesttg.setup()# to use --terragrunt-<option>, pass in tg_<option in snake case>tg.apply(output=False,tg_non_interactive=True)yieldtg.output()tg.destroy(auto_approve=True,tg_non_interactive=True)deftest_run_all_apply(run_all_apply_out):triggers=[o["triggers"]foroinrun_all_apply_out]assert[{'name':'foo','template':'sample template foo'}]intriggersassert[{'name':'bar','template':'sample template bar'}]intriggersassert[{'name':'one','template':'sample template one'},{'name':'two','template':'sample template two'}]intriggersassertlen(run_all_apply_out)==3CachingTheTerraformTestsetup,init,plan,apply,outputanddestroymethods have the ability to cache it's associate output to a local.tftest-cachedirectory. For subsequent calls of the method, the cached value can be returned instead of calling the actual underlyingterraformcommand. Using the cache value can be significantly faster than running the Terraform command again especially if the command is time-intensive.To determine if the cache should be used, first a hash value is generated using the currentTerraformTestinstance__init__and calling method arguments, file contents of thetfdirand file contents of anytf_var_fileorextra_filesmethod argument. The hash value is compared to the hash value of the cached instance's associated arguments. If the hash is the same then the cache is used, otherwise the method is executed.The benefits of the caching feature include:Faster setup time for testing terraform modules that don't change between testing sessionsWriting tests without worrying about errors within their test code resulting in the Terraform setup logic to run againPlease see the following example for how to use it:importpytestimporttftest@pytest.fixturedefoutput(fixtures_dir):tf=tftest.TerraformTest('apply',fixtures_dir,enable_cache=True)tf.setup(use_cache=True)tf.apply(use_cache=True)yieldtf.output(use_cache=True)tf.destroy(use_cache=True,**{"auto_approve":True})deftest_apply(output):value=output['triggers']assertlen(value)==2assertlist(value[0].keys())==['name','template']assertvalue[0]['name']=='one'CompatibilityStarting from version1.0.0Terraform0.12is required, and tests written with previous versions of this module are incompatible. Check theCHANGELOG.mdfile for details on what's changed.TestingTests use thepytestframework and have no other dependency except on the Terraform binary. The version used during development is in theDEV-REQUIREMENTS.txtfile.DisclaimerThis is not an officially supported Google product.