Terraform Testing the azurerm_eventgrid_topic.topic.input_mapping_fields

I am attempting to test the input mapping fields for azurerm_eventgrid_topic

run “full” {
command = plan
variables {
name = “custom-eventgrid”
resource_group_name = run.setup_rg.resource_group_name
input_schema = “CustomEventSchema”
input_mapping_fields = { id : “guid”
topic : “channel”
event_type : “datatype”
event_time : “dateTime”
data_version : “version”
subject : “event” }
input_mapping_default_values = { event_type : “event”,
subject : “CustomEvent”,
data_version : “1.0” }
}
assert {
condition = azurerm_eventgrid_topic.topic.input_mapping_fields == var.input_mapping_fields
error_message = “”
}

assert {
condition = azurerm_eventgrid_topic.topic.input_mapping_default_values == var.input_mapping_default_values
error_message = “”
}

here is the results:

Error: Test assertion failed

│ on tests/passthrough.tftest.hcl line 70, in run “full”:
│ 70: condition = azurerm_eventgrid_topic.topic.input_mapping_fields == tomap(var.input_mapping_fields)
│ ├────────────────
│ │ azurerm_eventgrid_topic.topic.input_mapping_fields is list of object with 1 element
│ │ var.input_mapping_fields is map of string with 6 elements



│ Error: Test assertion failed

│ on tests/passthrough.tftest.hcl line 75, in run “full”:
│ 75: condition = azurerm_eventgrid_topic.topic.input_mapping_default_values == var.input_mapping_default_values
│ ├────────────────
│ │ azurerm_eventgrid_topic.topic.input_mapping_default_values is list of object with 1 element
│ │ var.input_mapping_default_values is map of string with 3 elements

my question is what type is the input_mapping_fields, and why does it think my input is an object, with 1 element?

Hi @jimolson,

The type of these input_mapping_fields and input_mapping_default_values attributes is decided by the hashicorp/azurerm provider as part of its schema for azurerm_eventgrid_topic. I cannot say why those attributes have those types – I’m not famililar with this resource type – but the attributes have the types they have and so you’ll need to write your test assertions with that in mind.

Comparing values of non-primitive types using == is allowed in Terraform, but is also tricky: this operator will return true only if the two operands exactly match. That means they must be of identical types, and all of the nested elements/attributes must match exactly.

Since the schema of this resource type is not under your control and may evolve in future versions of the provider, I would therefore recommend against trying to compare the entire data structure because that will make your test fragile against future evolution of the provider.

Instead, I would suggest transforming the provider’s values into a simpler form that captures only the subset of data you want to check, and use explicit type conversion functions (like tostring, tomap, etc) to ensure that the types should agree on both sides of the == operator.

I’m not sure exactly what subset of the data is important, but here’s an example where I’ve arbitrarily decided that subject and event_type are the important attributes:

  condition = {
    subject    = tostring(azurerm_eventgrid_topic.topic.input_mapping_fields[0].subject)
    event_type = tostring(azurerm_eventgrid_topic.topic.input_mapping_fields[0].event_type)
  } == {
    subject    = tostring(var.input_mapping_fields.subject)
    event_type = tostring(var.input_mapping_fields.event_type)
  }

In the above example, the type of both == operands is object({ subject = string, event_type = string }), and so as long as those two attributes have equal values the test should pass.


Side-note: For a situation like this where you are comparing two parts of the live data of the module, rather than comparing live data with a constant value, this check might be better placed as a postcondition of azurerm_eventgrid_topic.topic, since it describes a general rule that applies regardless of what the input is, whereas assert blocks are intended for conditions that should hold just for this specific input.

The advantage of using inline conditions, instead of test assertions, is that then Terraform will also perform these checks at runtime, ensuring that you’ll catch any inconsistencies that arise as an error even if you didn’t think to include a test scenario describing a particular set of input, or if you forgot to run the tests before merging, or various other similar reasons.

You can read more about postconditions in Custom Condition Checks, but here’s an example of how the above example would look if written inline in your module instead:

resource "azurerm_eventgrid_topic" "topic" {
  # ...

  lifecycle {
    postcondition {
      condition = {
        subject    = tostring(self.input_mapping_fields[0].subject)
        event_type = tostring(self.input_mapping_fields[0].event_type)
      } == {
        subject    = tostring(var.input_mapping_fields.subject)
        event_type = tostring(var.input_mapping_fields.event_type)
      }
      error_message = "..."
    }
  }
}

terraform test will also fail a test scenario if any postconditions fail, so writing this test as a postcondition will cause it to run during testing and at runtime, whereas assert rules in test scenario files are only checked by terraform test.