Skip to main content
Infrastructure bugs are expensive to find in production and cheap to find at compile time. A bucket that should be named staging-assets but gets named prod-assets because an input default is wrong. A database identifier that doesn’t match what the application expects. A reference to a resource label that was renamed six months ago. These are the kinds of mistakes that break deployments, wake up on-call engineers, and take hours to diagnose. ubx’s native test blocks address this directly: write assertions in a .test.iac file alongside your stack, and ubx test compiles the stack with the specified inputs and verifies the resulting resource plan matches your expectations. No deployment required, no cloud credentials needed, no Pulumi plan to interpret — just fast, local, compile-time verification that your .iac source produces the infrastructure you intend.

What you’ll learn

  • How to write test blocks in .test.iac files
  • The inputs, assert, resource, action, attributes structure
  • How expect_error tests verify the compiler catches invalid configurations

Why this matters

ubx test runs entirely locally — no cloud credentials, no network, no apply. Tests run in milliseconds because they’re compile-time assertions against the planned resource graph, not integration tests against live infrastructure. Add them to CI to catch regressions before they reach staging.

The source code

unit "aws_s3_bucket_v2" "assets" {
  bucket = "${input.env}-assets"
  tags = {
    Environment = input.env
    ManagedBy   = "ubx"
  }
}

unit "aws_rds_instance" "db" {
  engine                  = "postgres"
  instance_class          = "db.t3.micro"
  identifier              = "${input.env}-db"
  storage_encrypted       = true
  backup_retention_period = 7
}

How it works

1

ubx test discovers *.test.iac files

Running ubx test scans the project directory for files matching *.test.iac. These files are compiled separately from the main stack — they never appear in ubx validate output or the generated Pulumi program.
2

Each test compiles the stack with the specified inputs

For test "s3_bucket_naming" { inputs = { env = "staging" } }, ubx compiles main.iac with env = "staging" and builds the planned resource graph — the same graph ubx plan would produce, but computed entirely locally without contacting AWS.
3

Assertions are checked against the planned resource graph

assert { resource = "aws_s3_bucket_v2.assets"; action = "create"; attributes = { bucket = "staging-assets" } } checks that the compiled plan includes a create action on aws_s3_bucket_v2.assets with bucket = "staging-assets". A mismatch fails the test with a clear diff.
4

expect_error tests verify compiler error behaviour

expect_error = "Undefined reference" compiles the given source snippet and asserts that the compiler produces an error matching that string. This guards against regressions in type-checker rules — if someone accidentally removes the undefined-reference check, this test fails.

Common mistakes

Test blocks use resource = "type.label" format — "aws_s3_bucket_v2.assets", not "unit.aws_s3_bucket_v2.assets". The unit keyword is omitted in test resource references, matching Pulumi’s internal resource address format.
Write tests for every non-trivial naming convention, every conditional expression that picks different values per environment, and every for_each expansion. These are the places most likely to silently produce wrong names when inputs change. A test with inputs = { env = "prod" } and inputs = { env = "dev" } covers both code paths in seconds.

Run it

ubx test               # runs all *.test.iac files, no cloud credentials needed
ubx test --verbose     # shows each assertion result
ubx validate           # test files are ignored — main stack only

What you learned

test blocks in *.test.iac files assert that compiled resource plans match expected attributes
ubx test runs entirely locally — no cloud credentials, no network, no apply
expect_error tests verify the compiler correctly catches invalid configurations

Next steps

AI plan summary and cost

Get a plain-English summary of your plan

Security scan

Enforce security rules at validate time