All skills
hashicorp avatar

/terraform-test

@4451cec official
by hashicorphashicorp/agent-skills880 stars
130

Comprehensive guide for writing and running Terraform tests. Use when creating test files (.tftest.hcl), writing test scenarios with run blocks, validating infrastructure behavior with assertions, mocking providers and data sources, testing module outputs and resource configurations, or troubleshooting Terraform test syntax and execution.

Use this Skill: https://skilld.dev/gh/hashicorp/agent-skills/terraform-test

This session only. Nothing lands on disk.

referencesEXAMPLES.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Example Test Suite

Complete example testing a VPC module with unit, integration, and mock tests.

Unit Tests (Plan Mode)

# tests/vpc_module_unit_test.tftest.hcl

variables {
  environment = "test"
  aws_region  = "us-west-2"
}

run "test_defaults" {
  command = plan

  variables {
    vpc_cidr = "10.0.0.0/16"
    vpc_name = "test-vpc"
  }

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR should match input"
  }

  assert {
    condition     = aws_vpc.main.enable_dns_hostnames == true
    error_message = "DNS hostnames should be enabled by default"
  }

  assert {
    condition     = aws_vpc.main.tags["Name"] == "test-vpc"
    error_message = "VPC name tag should match input"
  }
}

run "test_subnets" {
  command = plan

  variables {
    vpc_cidr        = "10.0.0.0/16"
    vpc_name        = "test-vpc"
    public_subnets  = ["10.0.1.0/24", "10.0.2.0/24"]
    private_subnets = ["10.0.10.0/24", "10.0.11.0/24"]
  }

  assert {
    condition     = length(aws_subnet.public) == 2
    error_message = "Should create 2 public subnets"
  }

  assert {
    condition     = length(aws_subnet.private) == 2
    error_message = "Should create 2 private subnets"
  }

  assert {
    condition = alltrue([
      for subnet in aws_subnet.private :
      subnet.map_public_ip_on_launch == false
    ])
    error_message = "Private subnets should not assign public IPs"
  }
}

run "test_outputs" {
  command = plan

  variables {
    vpc_cidr = "10.0.0.0/16"
    vpc_name = "test-vpc"
  }

  assert {
    condition     = output.vpc_id != ""
    error_message = "VPC ID output should not be empty"
  }

  assert {
    condition     = can(regex("^vpc-", output.vpc_id))
    error_message = "VPC ID should have correct format"
  }

  assert {
    condition     = output.vpc_cidr == "10.0.0.0/16"
    error_message = "VPC CIDR output should match input"
  }
}

run "test_invalid_cidr" {
  command = plan

  variables {
    vpc_cidr = "invalid"
    vpc_name = "test-vpc"
  }

  expect_failures = [
    var.vpc_cidr
  ]
}

Integration Tests (Apply Mode)

# tests/vpc_module_integration_test.tftest.hcl

variables {
  environment = "integration-test"
  aws_region  = "us-west-2"
}

run "integration_test_vpc_creation" {
  # command defaults to apply — creates real AWS resources

  variables {
    vpc_cidr = "10.100.0.0/16"
    vpc_name = "integration-test-vpc"
  }

  assert {
    condition     = aws_vpc.main.id != ""
    error_message = "VPC should be created with valid ID"
  }

  assert {
    condition     = aws_vpc.main.state == "available"
    error_message = "VPC should be in available state"
  }
}

Mock Tests (Plan Mode, No Credentials)

# tests/vpc_module_mock_test.tftest.hcl

mock_provider "aws" {
  mock_resource "aws_instance" {
    defaults = {
      id            = "i-1234567890abcdef0"
      instance_type = "t2.micro"
      ami           = "ami-12345678"
      public_ip     = "203.0.113.1"
      private_ip    = "10.0.1.100"
    }
  }

  mock_resource "aws_vpc" {
    defaults = {
      id                   = "vpc-12345678"
      cidr_block           = "10.0.0.0/16"
      enable_dns_hostnames = true
      enable_dns_support   = true
    }
  }

  mock_resource "aws_subnet" {
    defaults = {
      id                      = "subnet-12345678"
      vpc_id                  = "vpc-12345678"
      cidr_block              = "10.0.1.0/24"
      availability_zone       = "us-west-2a"
      map_public_ip_on_launch = false
    }
  }

  mock_data "aws_ami" {
    defaults = {
      id   = "ami-0c55b159cbfafe1f0"
      name = "ubuntu-focal-20.04-amd64"
    }
  }

  mock_data "aws_availability_zones" {
    defaults = {
      names = ["us-west-2a", "us-west-2b", "us-west-2c"]
    }
  }
}

run "test_instance_with_mocks" {
  command = plan

  variables {
    instance_type = "t2.micro"
    ami_id        = "ami-12345678"
  }

  assert {
    condition     = aws_instance.example.instance_type == "t2.micro"
    error_message = "Instance type should match input variable"
  }

  assert {
    condition     = aws_instance.example.id == "i-1234567890abcdef0"
    error_message = "Mock should return consistent instance ID"
  }
}

run "test_data_source_with_mocks" {
  command = plan

  assert {
    condition     = data.aws_ami.ubuntu.id == "ami-0c55b159cbfafe1f0"
    error_message = "Mock data source should return predictable AMI ID"
  }

  assert {
    condition     = length(data.aws_availability_zones.available.names) == 3
    error_message = "Should return 3 mocked availability zones"
  }

  assert {
    condition     = contains(data.aws_availability_zones.available.names, "us-west-2a")
    error_message = "Should include us-west-2a in mocked zones"
  }
}

run "test_outputs_with_mocks" {
  command = plan

  assert {
    condition     = output.vpc_id == "vpc-12345678"
    error_message = "VPC ID output should match mocked value"
  }

  assert {
    condition     = can(regex("^vpc-", output.vpc_id))
    error_message = "VPC ID output should have correct format"
  }
}

run "test_conditional_resources_with_mocks" {
  command = plan

  variables {
    create_bastion     = true
    create_nat_gateway = false
  }

  assert {
    condition     = length(aws_instance.bastion) == 1
    error_message = "Bastion should be created when enabled"
  }

  assert {
    condition     = length(aws_nat_gateway.nat) == 0
    error_message = "NAT gateway should not be created when disabled"
  }
}

run "test_tag_inheritance_with_mocks" {
  command = plan

  variables {
    common_tags = {
      Environment = "test"
      ManagedBy   = "Terraform"
    }
  }

  assert {
    condition = alltrue([
      for key in keys(var.common_tags) :
      contains(keys(aws_instance.example.tags), key)
    ])
    error_message = "All common tags should be present on instance"
  }
}

run "test_invalid_cidr_with_mocks" {
  command = plan

  variables {
    vpc_cidr = "invalid"
  }

  expect_failures = [
    var.vpc_cidr
  ]
}

run "setup_vpc_with_mocks" {
  command = plan

  variables {
    vpc_cidr = "10.0.0.0/16"
    vpc_name = "test-vpc"
  }

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR should match input"
  }
}

run "test_subnet_references_vpc_with_mocks" {
  command = plan

  variables {
    vpc_id      = run.setup_vpc_with_mocks.vpc_id
    subnet_cidr = "10.0.1.0/24"
  }

  assert {
    condition     = aws_subnet.example.vpc_id == run.setup_vpc_with_mocks.vpc_id
    error_message = "Subnet should reference VPC from previous run"
  }
}

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides a comprehensive guide for Terraform testing. It follows security best practices, particularly in its CI/CD documentation and the use of mock providers for isolated testing.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    1/1 file flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 4451cec. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 2 months ago
Other metadata
metadata
{
  "lifecycle-status": "active",
  "copyright": "Copyright IBM Corp. 2026",
  "version": "0.0.2"
}

README badge

README badge for hashicorp/agent-skills/terraform-test

Guides writing and running Terraform tests using the built-in testing framework (.tftest.hcl files), including run blocks for validating configuration, assertions to check resource behavior, and mock providers to simulate infrastructure without real resources. Covers plan-mode unit tests, apply-mode integration tests, testing outputs and validation rules, and structuring tests in CI/CD pipelines.

Generated from the current SKILL.md.

What Terraform version do I need for this skill?
Terraform 1.6.0+ supports the testing framework. Mock providers require Terraform 1.7.0 or later. The skill includes version checks and will guide you to skip unsupported features if your version is below 1.7.
Can I test modules from git repositories or HTTP sources?
No. Test files only support local module paths and registry modules. You must convert git or HTTP sources to local modules before testing them.
What's the difference between plan mode and apply mode tests?
Plan mode (`command = plan`) validates logic without creating real resources — faster and no credentials needed. Apply mode (`command = apply`) creates actual infrastructure and is slower but tests real resource behavior. Use unit tests in plan mode and integration tests in apply mode.
How do I test modules in parallel?
Set `parallel = true` on individual run blocks (Terraform 1.9.0+) or enable test-wide parallel execution with `test { parallel = true }`. Parallel tests use isolated state files and should not depend on each other.
How are resources cleaned up after tests run?
Resources created by apply-mode tests are destroyed in reverse run block order automatically. Use `terraform test -no-cleanup` to skip cleanup for debugging.

Generated from the current SKILL.md. These answers refresh after source changes.