Day 18 of my Terraform journey was one of the most exciting so far because it brought everything together: local testing, real AWS validation, and a CI/CD pipeline that runs automatically in GitHub Actions.
In Chapter 9 of Terraform: Up & Running, Yevgeniy Brikman explains that manual testing is necessary, but it does not scale. Once infrastructure grows beyond what one person can test by hand every time, you need automation that catches regressions early and gives the whole team confidence to move faster.
That was the focus of today.
I implemented all three layers of Terraform testing:
- unit tests with
terraform test - integration tests with Terratest
- end-to-end tests with Terratest
- a GitHub Actions workflow to run them automatically
GitHub reference:
👉 GitHub Day 18 Link
Why One Test Type Is Not Enough
One of the clearest lessons from today is that no single test layer is enough on its own.
If you only use unit tests:
- they are fast and cheap
- but they do not prove real AWS behavior
If you only use integration tests:
- they prove module composition
- but they still do not test the full stack from the outside
If you only use end-to-end tests:
- they give the strongest confidence
- but they are slower, more expensive, and harder to debug
The right strategy is to use all three.
Layer 1: Unit Tests with terraform test
Terraform 1.6+ includes native testing through .tftest.hcl files, which makes lightweight unit-style testing much easier.
For Day 18, I used terraform test to validate module behavior without creating any real infrastructure.
These tests checked things like:
- ASG naming logic
- instance type propagation
- security group port configuration
- variable validation for invalid environments
Example command:
terraform -chdir=day_18/modules/services/webserver-cluster test
What I liked about this layer:
- very fast
- no AWS cost
- no external dependencies
- great for checking Terraform logic early
Tradeoff:
- it does not prove that AWS will accept and run the infrastructure correctly
So unit tests are excellent for fast feedback, but they only cover part of the story.
Layer 2: Integration Tests with Terratest
The next layer was Terratest integration testing.
Here, the goal was to deploy real infrastructure and verify that the reusable modules worked correctly together in AWS.
For my integration test, I used:
- the Day 18 webserver cluster module
- a lightweight example root module
- the default VPC as the test harness
The test:
- applied the infrastructure
- read outputs like
alb_dns_name - sent HTTP requests to the deployed ALB
- verified the expected response
- destroyed the resources afterward
This gave me confidence that:
- Terraform could really apply the stack
- the ALB, ASG, target group, and alarms worked together
- the app was actually reachable
Tradeoff:
- slower than unit tests
- creates real AWS resources
- costs money
- needs cleanup discipline
Still, this is the layer where Terraform starts moving from “logic looks right” to “real infrastructure works.”
Layer 3: End-to-End Tests with Terratest
The most complete layer was the end-to-end test.
Instead of using existing networking, this test deployed:
- a fresh VPC
- subnets
- the webserver stack on top of that VPC
- the full public application path
Then it verified that the final app response was correct from the outside.
This was the strongest test because it checked the complete system, not just a subset of it.
It also exposed a real issue:
my first end-to-end run failed because the subnet CIDRs did not fit inside the VPC CIDR range. AWS rejected them with an InvalidSubnet.Range error.
That was a very useful reminder that end-to-end tests often catch the kinds of real cloud issues that smaller tests miss.
After fixing the VPC CIDR input, the end-to-end test passed and cleaned up correctly.
Tradeoff:
- slowest test layer
- most expensive
- most realistic
- best confidence
This is why end-to-end tests are powerful, but you do not want to rely on them alone for every kind of feedback.
The CI/CD Pipeline That Ties It Together
After getting the local tests working, I built a GitHub Actions workflow to automate the testing pipeline.
The workflow runs:
- unit tests on pull requests
- unit, integration, and end-to-end tests on pushes to
main - manual workflow runs when needed
That setup gave me a nice balance:
- PRs stay faster and cheaper
- merges to
mainget full confidence checks
One of the best parts of today was seeing the pipeline work the way it should:
- local tests passed
- the first GitHub Actions run failed
- I fixed the workflow
- opened a pull request from a feature branch
- PR checks ran
- merged into
main - GitHub Actions ran all three layers successfully
That was a great end-to-end learning moment for both Terraform testing and CI/CD.
What Each Layer Contributed
Here is how I now think about the testing stack.
Unit tests
Tool:
terraform test
Best for:
- validation logic
- naming
- output expectations
- fast feedback
Integration tests
Tool:
- Terratest
Best for:
- real AWS deployment checks
- reusable module composition
- output and connectivity verification
End-to-end tests
Tool:
- Terratest
Best for:
- full-stack confidence
- fresh environment testing
- user-visible behavior
- catching real infrastructure wiring problems
That layered approach is what makes the whole system strong.
My Main Takeaway
The biggest lesson from Day 18 was this:
good Terraform testing is not about choosing one “best” test type.
It is about using the right mix of test layers for the right level of confidence.
- unit tests are fast and free, but test less
- integration tests are more realistic, but slower and costlier
- end-to-end tests are the most thorough, but also the heaviest
The right strategy uses all three.
And once those tests are connected to CI/CD, infrastructure changes stop being something you only hope will work and start becoming something your pipeline can prove.
Full Code
GitHub reference:
👉 GitHub Day 18 Link
Follow My Journey
This is Day 18 of my 30-Day Terraform Challenge.
See you on Day 19.
Top comments (0)