Skip to main content

Terraform Test Mocks

·1200 words·6 mins
Terraform Test Mocks Overrides

Introduction
#

The Terraform test framework has been around for more than one year, and I have previously written extensively on the topic.

A Comprehensive Guide to Testing in Terraform: Keep your tests, validations, checks, and policies in order
·5480 words·26 mins
Hashicorp Terraform Testing Test Policy Sentinel
Testing Framework in Terraform 1.6: A deep-dive
·4238 words·20 mins
Hashicorp Terraform Testing Test
Take your testing to the cloud
·3205 words·16 mins
Hashicorp Terraform Testing Test
HashiTalks 2024: Mastering Terraform Testing, a layered approach to testing complex infrastructure
·6685 words·32 mins
Hashicorp Hashitalks Terraform Testing

A part of the test framework that I have not covered is what was introduced in Terraform 1.7. Using Terraform test mocks allow you to mock providers, resources and data sources in your test files.

In this blog post I will go through the additions that Terraform 1.7 brought to the Terraform test framework.

What are Terraform test mocks?
#

A mock in the Terraform test framework is similar to a mock in any other test framework. Mocking is the practice of replacing an actual instance of something with a fake, or mocked, instance of that something. In the context of the Terraform test framework the “something” is a provider, a module, a resource or a data source.

Why would you want to, or need to, mock something in your tests?

Ideally you should run full tests using real resources, data sources and providers. This is the only way you will ever be sure that everything works end-to-end. However, using mocks can significantly speed up your tests.

A concrete example where mocking makes sense is if you have a large infrastructure, perhaps with multiple Kubernetes clusters, and you want to run a test related to a load balancer that directs traffic to one of your Kubernetes clusters. In this case it could make sense to mock some, or all, of the Kubernetes clusters.

Note that in some cases it might not be possible to mock away certain resources or data sources. This is true when you want to run tests for a resource that has dependencies to other resources. In this case it might not be possible to replace the dependencies with mocks.

Apart from pure mocks the Terraform test framework also supports overrides. In the following subsections we will see how both of these work.

Mock a provider
#

If you want to mock a provider you use the new mock_provider block in your Terraform test files:

mock_provider "azurerm" {}

In this example we have mocked the azurerm provider. All interactions with this provider will be replaced by dummy data.

You can mix real and mocked providers of the same type in the same test configuration. An example of how to do this:

provider "azurerm" {}

mock_provider "azurerm" {
  alias = "mocked"
}

As with a normal Terraform configuration you need to provide an alias for any additional provider configurations you include. In your tests you can either use the default provider (the provider with no alias argument), or specify that a different provider should be used:

run "test_with_mocked_provider" {
  providers = {
    azurerm = azurerm.mocked
  }
}

Mock a resource
#

If you want to be more specific about what data a mocked provider returns you can mock a specific resource using the mock_resource block:

mock_provider "azurerm" {
  mock_resource "azurerm_resource_group" {
    defaults = {
      name = "rg-test-group"
    }
  }
}

The mock_resource block accepts a defaults argument where you can specify what data should be returned when a resource of the mocked type is used. In the example above we specify that the name attribute of the azurerm_resource_group resource should be returned as rg-test-group.

Note that the code above mocks all instances of azurerm_resource_group resources.

Mock a data source
#

Similar to how you mock a resource you can also mock a data source using the mock_data block:

mock_provider "azurerm" {
  mock_data "azurerm_resource_group" {
    defaults = {
      name = "rg-test-group"
    }
  }
}

The mock_data block accepts the same default argument as the mock_resource block.

Note that as with mocked resources, the code above mocks all instances of the azurerm_resource_group data source.

Overrides
#

Mocks replace all instances of a given resource type or data source type. This might not be desirable for some tests. For these situations the Terraform test framework supports overrides.

An override is similar to a mock, but they allow you to replace a specific instance of a module, resource or data source. This allows you to use a real provider but specifically target a few objects and replace them with mocks.

To override a data source, use the override_data block:

mock_provider "azurerm" {
  override_data {
    target = data.azurerm_resource_group.this
    values = {
      name = "rg-overridden-name"
    }
  }
}

All override blocks take a target argument that specifies the address of the object to be overridden.

To override a resource, use the override_resource block:

mock_provider "azurerm" {
  override_resource {
    target = data.azurerm_resource_group.this
    values = {
      name = "rg-overridden-name"
    }
  }
}

Both the override_data and override_resource block accepts a values argument where you can configure specific values for attributes on the object.

Finally, you can override a whole module with the override_module block:

override_module {
  target = module.networking
  outputs = {
    cidr = "10.100.10.0/24"
  }
}

The override_module block has an outputs argument where you can override the output values from this module. Note that the override_module is defined in the root of a test file, not as part of the mock_provider block. In fact, you can also define override_resource and override_data blocks in the root of the test file:

override_data {
  target = data.azurerm_resource_group.this
  values = {
    name = "rg-overridden-name"
  }
}

override_resource {
  target = data.azurerm_resource_group.this
  values = {
    name = "rg-overridden-name"
  }
}

override_module {
  target = module.networking
  outputs = {
    cidr = "10.100.10.0/24"
  }
}

run "this_is_my_test" {
  # ...
}

You can also define override blocks inside run blocks. Overrides defined in a given run block will take precedence over any other override block for the same module, resource or data source:

# this block is defined at the root (or global) level
override_data {
  target = data.azurerm_resource_group.this
  values = {
    name = "rg-overridden-name"
  }
}

run "this_is_my_test" {
  # this block is defined inside of a run block and it will take precedence
  # in this specific test
  override_data {
    target = data.azurerm_resource_group.this
    values = {
      name = "rg-overridden-name"
    }
  }
}

Conclusions
#

Terraform test mocks can help you speed up your test runs. They allow you to focus on what is important in your current test and allow you to skip the resources and data sources that do not add anything to the test.

There are a number of new blocks available for your Terraform test files to work with mocks:

  • mock_provider to mock a given provider
  • mock_resource to mock all resources of a specific type
  • mock_data to mock all data sources of a specific type
  • override_module to mock a specific module
  • override_resource to mock a specific resource
  • override_data to mock a specific data source

Use mocks sparingly and only where it makes sense to do so.

Read more about mocks in the official documentation.

Mattias Fjellström
Author
Mattias Fjellström
Cloud architect · Author · HashiCorp Ambassador