--- description: >- The tofu test command performs integration tests of OpenTofu modules. --- import CodeBlock from '@theme/CodeBlock'; import SimpleMain from '!!raw-loader!./examples/simple/main.tf' import SimpleTest from '!!raw-loader!./examples/simple/main.tftest.hcl' import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FlatLayout from '!!raw-loader!./flat-layout.txt' import FlatLayoutModule from '!!raw-loader!./flat-layout-module.txt' import NestedLayout from '!!raw-loader!./nested-layout.txt' import NestedLayoutModule from '!!raw-loader!./nested-layout-module.txt' import ModuleHarness from '!!raw-loader!./examples/module/test-harness/harness.tf' import ModuleMain from '!!raw-loader!./examples/module/main.tf' import ModuleTest from '!!raw-loader!./examples/module/main.tftest.hcl' import PlanMain from '!!raw-loader!./examples/plan/main.tf' import PlanTest from '!!raw-loader!./examples/plan/main.tftest.hcl' import OfflineMain from '!!raw-loader!./examples/offline/main.tf' import OfflineTest from '!!raw-loader!./examples/offline/main.tftest.hcl' import ProviderAliasMain from '!!raw-loader!./examples/provider_alias/main.tf' import ProviderAliasTest from '!!raw-loader!./examples/provider_alias/main.tftest.hcl' import VariablesMain from '!!raw-loader!./examples/variables/main.tf' import VariablesTest from '!!raw-loader!./examples/variables/main.tftest.hcl' import RunInVariablesMain from '!!raw-loader!./examples/run_in_variables/mod/main.tf' import RunInVariablesTest from '!!raw-loader!./examples/run_in_variables/main.tftest.hcl' import ExpectFailureVariablesMain from '!!raw-loader!./examples/expect_failures_variables/main.tf' import ExpectFailureVariablesTest from '!!raw-loader!./examples/expect_failures_variables/main.tftest.hcl' import ExpectFailureResourcesMain from '!!raw-loader!./examples/expect_failures_resources/main.tf' import ExpectFailureResourcesTest from '!!raw-loader!./examples/expect_failures_resources/main.tftest.hcl' import OverrideResourceMain from '!!raw-loader!./examples/override_resource/main.tf' import OverrideResourceTest from '!!raw-loader!./examples/override_resource/main.tftest.hcl' import MockProviderMain from '!!raw-loader!./examples/mock_provider/main.tf' import MockProviderTest from '!!raw-loader!./examples/mock_provider/main.tftest.hcl' import OverrideModuleMain from '!!raw-loader!./examples/override_module/main.tf' import OverrideModuleTest from '!!raw-loader!./examples/override_module/main.tftest.hcl' import OverrideModuleBucketMeta from '!!raw-loader!./examples/override_module/bucket_meta/main.tf' # Command: test The `tofu test` command lets you test your OpenTofu configuration by creating real infrastructure and checking that the required conditions (assertions) are met. Once the test is complete, OpenTofu destroys the resources it created. ## Usage Usage: `tofu test [options]`. This command will execute all `*.tftest.hcl`, `*.tftest.json`, `*.tofutest.hcl`, and `*.tofutest.json` files in the current directory or in a directory called `tests`. You can customize this behavior using the [options](#options) below. :::info Example Consider the following simple example which creates a `test.txt` file from `main.tf` and then checks that the main code has successfully performed its job from `main.tftest.hcl`. {SimpleMain} {SimpleTest} You can run `tofu init` followed by `tofu test` to execute the test which will apply the `main.tf` file and test it against the assertion in `main.tftest.hcl`. This is just a simple illustration. You can find more comprehensive examples below. ::: ## Extension Precedence When both `.tftest.hcl` and `.tofutest.hcl` files with the same base name are present in a directory, OpenTofu will prioritize the `.tofutest.hcl` file and ignore the `.tftest.hcl` file. For example: - If both `main.tftest.hcl` and `main.tofutest.hcl` exist in the same directory, OpenTofu will only load `main.tofutest.hcl` and ignore `main.tftest.hcl`. This ensures that `.tofu` files always take precedence over `.tf` files when both are available. This scenario can be useful for module authors who want their modules to support both OpenTofu and Terraform and want to create different tests for each. The same rule applies to JSON-based test files: - if both `main.tftest.json` and `main.tofutest.json` exist in the same directory, OpenTofu will only load `main.tofutest.json` and ignore `main.tftest.json`. ## Options * `-test-directory=path` Set the test directory (default: "tests"). OpenTofu will search for test files in the specified directory and also the current directory when you run `tofu test`. The path should be relative to the current working directory. * `-filter=testfile` Specify an individual test file to run. Use this option multiple times to specify more than one file. The path should be relative to the current working directory. :::warning If `-filter` is used alongside `-test-directory=path`, any filters for test files in the \ must be prepended by the \, as in: ``` tofu test -test-directory=extra-tests -filter=extra-tests/a.tftest.hcl ``` ::: * `-var 'foo=bar'` Set an input variable of the root module. Specify this option multiple times to add more than one variable. * `-var-file=filename` Set multiple variables from the specified file. In addition to this file, OpenTofu automatically loads `terraform.tfvars` and `*.auto.tfvars`. Use this option multiple times to specify more than one file. * `-json` Change the output format to JSON. * `-no-color` Disable colorized output in the command output. * `-verbose` Print the plan or state for each test run block as it executes. :::note Use of variables in [module sources](../../../language/modules/sources.mdx#support-for-variable-and-local-evaluation), [backend configuration](../../../language/settings/backends/configuration.mdx#variables-and-locals), or [encryption block](../../../language/state/encryption.mdx#configuration) requires [assigning values to root module variables](../../../language/values/variables.mdx#assigning-values-to-root-module-variables) when running `tofu test`. ::: ## Directory structure The `tofu test` command supports two directory layouts, flat or nested: This layout places the \*.tftest.hcl test files directly next to the \*.tf files they test. There are no rules that each \*.tf file must have its own test file, but it is a good practice to follow. {FlatLayout} This layout places the \*.tftest.hcl files in a separate tests directory. Similar to the flat layout, there are no rules that each \*.tf file must have its own test file, but it is a good practice to follow. {NestedLayout} ### Testing modules When testing modules, you can use one of the above directory structures for each module: With this layout, run tofu test -test-directory=./path/to/module to test the module in question. {FlatLayoutModule} With this layout, change your working directory to your module path and run tofu test to test the module in question. {NestedLayoutModule} :::tip Hint You can use the `-filter=sometest.tftest.hcl` option to run a limited set of test files. Use the option multiple times to run more than one test file. ::: ## The `*.tftest.hcl` / `*.tofutest.hcl` file structure The testing language of OpenTofu is similar to the main OpenTofu language and uses the same block structure. A test file consists of: * The **[`run` blocks](#the-run-block)**: define your tests. * A **[`variables` block](#the-variables-and-runvariables-blocks)** (optional): define variables for all tests in the current file. * The **[`provider` blocks](#the-providers-block)** (optional): define the providers to be used for the tests. * The **[`mock_provider` blocks](#the-mock_provider-blocks)** (optional): define the providers to be mocked. * The **[`override_resource` blocks](#the-override_resource-and-override_data-blocks)** (optional): define the resources to be overridden. * The **[`override_data` blocks](#the-override_resource-and-override_data-blocks)** (optional): define the data sources to be overridden. * The **[`override_module` blocks](#the-override_module-block)** (optional): define the module calls to be overridden. ### The `run` block A `run` block contains a single test case which runs either `tofu apply` or `tofu plan` and then evaluates all `assert` blocks. Once the test is complete, it uses `tofu destroy` to remove the temporarily created resources. A `run` block consists of the following elements: | Name | Type | Description | |:------------------------------------------------------------------------|:------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`assert`](#the-runassert-block) | block | Defines assertions that check if your code (e.g. `main.tf`) created the infrastructure correctly. If you do not specify any `assert` blocks, OpenTofu simply applies the configuration without any assertions. | | [`module`](#the-runmodule-block) | block | Overrides the module being tested. You can use this to load a helper module for more elaborate tests. | | [`expect_failures`](#the-runexpect_failures-list) | list | A list of resources that should fail to provision in the current run. | | [`variables`](#the-variables-and-runvariables-blocks) | block | Defines variables for the current test case. See the [variables section](#variables). | | [`command`](#the-runcommand-setting-and-the-runplan_options-block) | `plan` or `apply` | Defines the command which OpenTofu will execute, `plan` or `apply`. Defaults to `apply`. | | [`plan_options`](#the-runcommand-setting-and-the-runplan_options-block) | block | Options for the `plan` or `apply` operation. | | [`providers`](#the-providers-block) | object | Aliases for providers. | | [`override_resource`](#the-override_resource-and-override_data-blocks) | block | Defines a resource to be overridden for the run. | | [`override_data`](#the-override_resource-and-override_data-blocks) | block | Defines a data source to be overridden for the run. | | [`override_module`](#the-override_module-block) | block | Defines a module call to be overridden for the run. | ### The `run.assert` block You can specify `assert` blocks inside your `run` block to test the state of your infrastructure after the `apply` or `plan` operation is complete. There is no theoretical limit to the number of blocks you can define. Each block requires the following two attributes: 1. The `condition` is an [OpenTofu condition](../../../language/expressions/custom-conditions.mdx#condition-expressions) which should return `true` for the test to pass, `false` for the test to fail. The condition **must** reference a resource, data source, variable, output or module from the main code, otherwise OpenTofu will refuse to run the test. 2. The `error_message` is a string explaining what happened when the test fails. :::tip Example As a simple example, you can write an `assert` block as follows: {SimpleTest} {SimpleMain} ::: Please note that conditions only let you perform basic checks on the current OpenTofu state and use OpenTofu functions. **You cannot define additional data sources directly in your test code.** To work around this limitation, you can use [the `module` block](#the-runmodule-block) in order to load a helper module. ### The `run.module` block In some cases you may find that the tools provided in the [condition expression](../../../language/expressions/custom-conditions.mdx#condition-expressions) are not enough to test if your code created the infrastructure correctly. You can use the `module` block to override the main module `tofu test` loads. This gives you the opportunity to create additional resources or data sources that you can use in your `assert` conditions. Its syntax is similar to loading modules in normal OpenTofu code: ```hcl run "test" { module { source = "./some-module" } } ``` The `module` block has the following two attributes: * The `source` attribute points to the directory of the module to load or any other [module source](../../../language/modules/sources.mdx). * The `version` specifies the version of the module you want to use. :::warning Note You cannot pass parameters directly in the `module` block as you may be used to from the normal OpenTofu code. Instead, you should use the [`variables` block](#the-variables-and-runvariables-blocks) to pass parameters to your module. ::: :::tip Example In this example project the `main.tf` file creates a Docker container with an `nginx` image exposed on port 8080. The `main.tftest.hcl` file needs to test if the webserver actually starts properly, but it cannot do that without a helper module. To create the `http` data source, the `main.tftest.hcl` file loads the `test-harness` module. The test helper then loads the main module and adds the data source to check the HTTP response. Note that the data source in the `test-harness` has an explicit dependency on `module.main` to make sure that the data source only returns once the main module has finished its work. {ModuleMain} {ModuleTest} {ModuleHarness} This project uses a [third-party provider](https://github.com/kreuzwerker/terraform-provider-docker) to launch the container. You can run it locally if you have a Docker Engine installed. ::: ### The `variables` and `run.variables` blocks The code under test (e.g. `main.tf`) will often have variable blocks that you need to fill from your test case. You can provide variables to your test run using any of the following methods: | Order | Source | |:------:|:-------------------------------------------------------------------------------------------------------------------------------| | 1 | Environment variables with the `TF_VAR_` prefix. | | 2 | tfvar files specified in the current directory: `terraform.tfvars` and `*.auto.tfvars`. | | 3 | tfvar files specified in the tests directory: `tests/terraform.tfvars` and `tests/*.auto.tfvars`. | | 4 | Commandline variables defined using the flag `-var`, and the variables defined in the files specified by the flag `-var-file`. | | 5 | The variables from the `variables` block in a test file. | | 6 | The variables from the `variables` block in `run` block. | OpenTofu evaluates the variables in the order listed above, so you can use it to override the previously set variable. For example: {VariablesTest} {VariablesMain} #### The `run` block outputs for variables It is also possible to use the earlier-executed `run` block module outputs to set another `run` block `variables` values. This can be useful when you need to pass values between different test cases. {RunInVariablesTest} {RunInVariablesMain} ### The `run.expect_failures` list You can use `expect_failures` inside a `run` block to test [custom conditions](../../../language/expressions/custom-conditions.mdx) such as preconditions and postconditions or input variable validation rules. For example, the test case below checks if the `instances` input variable correctly fails validation when defined as a negative number: {ExpectFailureVariablesTest} {ExpectFailureVariablesMain} The example below checks if the misconfigured healthcheck fails. This ensures that the health check does not always return, even when it is running against the wrong endpoint. {ExpectFailureResourcesTest} {ExpectFailureResourcesMain} :::note Configured condition checks only The `expect_failure` argument is only for testing failures of [custom conditions](../../../language/expressions/custom-conditions.mdx) written in the configuration. It does not test problems detected by validation logic inside providers. ::: ### The `run.command` setting and the `run.plan_options` block By default, `tofu test` uses `tofu apply` to create real infrastructure. In some cases, for example if the real infrastructure is very expensive or impossible to run for testing purposes, it can be useful to only run `tofu plan` instead. You can use the `command = plan` setting to perform a plan instead of an apply. The following example tests if the variable is correctly passed to the `docker_image` resource without actually applying the plan: {PlanTest} {PlanMain} Regardless of the `command` setting, you can use the `plan_options` block to specify the following additional options for both modes: | Name | Description | |:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------| | mode | Change this option from `normal` (default) to `refresh-only` in order to only refresh the local state from the remote infrastructure. | | refresh | Set this option to `false` to disable checking for external changes in relation to the state file. Similar to `tofu plan -refresh=false`. | | replace | Force replacing the specified list of resources, such as `[docker_image.build]` in the above example. Similar to `tofu plan -replace=docker_image.build`. | | target | Limit planning to the specified list of modules or resources. Similar to `tofu plan -target=docker_image.build`. | :::tip Tip You can use these options in conjunction with [provider overrides](#the-providers-block) to create fully offline tests. See the [Providers section below](#the-providers-block) for an example. ::: ### The `providers` block In some cases you may want to override provider settings for test runs. You can use the `provider` blocks outside of `run` block to provide additional configuration options for providers, such as credentials for a test account. ```hcl provider "aws" { // Add additional settings here } ``` This feature can also enable partially or fully offline tests if the provider supports it. The following example illustrates a fully offline test with the AWS provider and an S3 bucket resource: {OfflineTest} {OfflineMain} #### Provider aliases In addition to provider overrides, you can alias providers in order to replace them with a different provider inside your `run` block. This is useful when you want to have two provider configurations within the same test file and switch between them. In the example below, the `sockettest` test case loads a different Docker provider configuration than the rest of the file. {ProviderAliasTest} {ProviderAliasMain} #### References to `run` module outputs and variables in `provider` blocks You can reference `var` (variables) and the `run` block module outputs in `provider` blocks to set up test providers based on dynamic configuration. ```hcl variables { region = "us-west-2" } provider "aws" { region = var.region access_key = run.setup.access_key secret_key = run.setup.secret_key } run "setup" { # `mod` module has outputs access_key and secret_key module { source = "./mod" } # Actual run block behavior, such as asserts, are skipped for simplicity } ``` ### The `mock_provider` blocks A `mock_provider` block allows you to replace provider configuration by a mocked one. In such scenario, creation and retrieval of provider resources and data sources will be skipped. Instead, OpenTofu will automatically generate all computed attributes and blocks to be used in tests. :::tip Note Learn more on how OpenTofu produces [automatically generated values](#automatically-generated-values). ::: For Mock Providers, the `alias` and `for_each` fields are supported. However, the `for_each` attribute is only supported when an `alias` is also used. The combination of these two will allow for the use of [provider instances](../../../language/providers/configuration/#for_each-multiple-instances-of-a-provider-configuration) on the test suite. Mock providers also support `mock_resource` and `mock_data` blocks. In some cases, you may want to use default values instead of automatically generated ones by passing them inside `defaults` field of `mock_resource` or `mock_data` blocks. Additionally, you can use `override_resource` and `override_data` blocks to override resources or data sources in the scope of a single provider. Read more about overriding in [the next section](#the-override_resource-and-override_data-blocks). In the example below, we test if the bucket name is correctly passed to the resource without actually creating it: {MockProviderTest} {MockProviderMain} ### The `override_resource` and `override_data` blocks In some cases you may want to test your infrastructure with certain resources or data sources being overridden. You can use the `override_resource` or `override_data` blocks to skip creation and retrieval of these resources or data sources using the real provider. Instead, OpenTofu will automatically generate all computed attributes and blocks to be used in tests. :::tip Note Learn more on how OpenTofu produces [automatically generated values](#automatically-generated-values). ::: These blocks consist of the following elements: | Name | Type | Description | |:--------:|:------------:|------------------------------------------------------------------------------------------------------------------------| | target | reference | Required. Address of the target resource or data source to be overridden. | | values | object | Custom values for computed attributes and blocks to be used instead of automatically generated. | You can use `override_resource` or `override_data` blocks for the whole test file or inside a single `run` block. The latter takes precedence if both specified for the same `target`. In the example below, we test if the bucket name is correctly passed to the resource without actually creating it: {OverrideResourceTest} {OverrideResourceMain} :::warning Limitation You cannot use `override_resource` or `override_data` with a single instance of a resource or data source. Each instance of a resource or data source must be overridden. ::: ### Automatically generated values Mocking resources and data sources requires OpenTofu to automatically generate computed attributes without calling respective providers. When generating these values, OpenTofu cannot follow custom provider logic, so it uses simple rules based on value type: | Attribute type | Generated value | |:------------------:|---------------------------------------------------------------------| | number | `0` | | bool | `false` | | string | A random alpha-numeric string. | | list | An empty list. | | map | An empty map. | | set | An empty set. | | object | An object with its fields populated by the same logic recursively. | | tuple | An empty tuple. | :::tip Note You can set custom values to use instead of automatically generated ones via respective mock or override fields. Keep in mind, it's only possible for computed attributes and configuration values cannot be changed. ::: ### The `override_module` block In some cases you may want to test your infrastructure with certain module calls being overridden. You can use the `override_module` block to ignore all the configuration provided by called module. In this case, OpenTofu will use custom values specified in the `override_module` block as module outputs. The block consist of the following elements: | Name | Type | Description | |:--------:|:------------:|------------------------------------------------------------------------------------------------------------------------| | target | reference | Required. Address of the target module call to be overridden. | | outputs | object | Values to be used as module call outputs. If an output is not specified, OpenTofu will set it to `null` by default. | You can use `override_module` block for the whole test file or inside a single `run` block. The latter takes precedence if both specified for the same `target`. In the example below, we test if the bucket name is correctly passed from the module without actually calling it: {OverrideModuleTest} {OverrideModuleMain} {OverrideModuleBucketMeta} :::warning Limitation You cannot use `override_module` with a single instance of a module call. Each instance of a module call must be overridden. :::