This project provides RSpec helpers for unit testing terraform plan output. It is born out of the pain of trying to
create reusable terraform modules and having them break or behave in unexpected ways when the plans have not been
reviewed for all possible use cases. rspec-terraform
gives you a way to write expectations for your terraform
plans and automate validation prior to attempting to go live.
Install the gem and add to a project Gemfile by executing:
$ bundle add rspec-terraform
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install rspec-terraform
Let's say that we've created a nonsensical module that creates an S3 bucket and hides all the details behind some absurd params:
# main.tf
variable "is_this_a_test" {
type = string
default = "please-no"
}
resource "aws_s3_bucket" "what" {
bucket = var.is_this_a_test
tags = {
Name = var.is_this_a_test
Environment = "My Brain Hurts"
}
}
Then, we might create some example configuration files for documentation and testing's sake which utilizes your custom module:
# examples/my-module/main.tf
module "my-module" {
source = "../my-custom-module"
is_this_a_test = "most-definitely"
}
module "my-other-module-with-defaults" {
source = "../my-custom-module"
}
Now, instead of running a plan manually, with rspec-terraform
, we can add require 'rspec/terraform
to our
spec_helper.rb
file and then write specs:
require 'spec_helper'
RSpec.describe "my-terraform-module" do
# If you do not want to run the plan for every `it` block, use this approach. Otherwise you can use `subject` or
# `it(:plan) { terraform_plan('my-module') }`
before :all do
@plan = terraform_plan('my-module')
end
it "returns a plan successfully" do
expect(@plan).to be_a(RubyTerraform::Models::Plan)
end
context "resources" do
let(:buckets) { @plan.resource_changes_matching(type: "aws_s3_bucket") }
let(:mod_bucket) { buckets.first.change.after }
let(:default_bucket) { buckets.last.change.after }
it "creates the required buckets" do
expect(buckets.count).to eq(2)
expect(buckets).to all(be_create)
end
it "uses the parameters passed" do
expect(bucket[:id]).to eq('most-definitely')
end
it "uses defaults" do
expect(bucket[:id]).to eq('please-no')
end
end
end
You can then run rspec as you normally would!
To understand the data structure returned by the plan, please refer to the RubyTerraform project's models. However, for the most part, you
can assume traversal in the change objects (as used above) to match the terraform output generated by plan show <plan> -json
(documented here).
I would heartily recommend traversing the code for RubyTerraform's models as there are a number of helper methods that make testing and scoping resources less painful.
RSpec.configure do |config|
# set the path to your terraform binary
config.terraform_binary = 'path/to/terraform/executable' # default: "/Users/`whoami`/.asdf/shims/terraform"
# disable plan output during the test run. Useful to enabled during initial testing, but can get pretty noisy
config.silence_terraform_output = true # default: false
# base directory for terraform examples, relative to where rspec is executed
config.terraform_examples_dir = "my/terraform/examples" # default: 'examples'
end
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rspec-terraform.
Many thanks to the folks behind RubyTerraform. The project code is a
joy to read and it is surprising to find so many unassuming gems like the entirely unexposed [models
code](https://github.
com/infrablocks/ruby_terraform/tree/main/lib/ruby_terraform/models)!
The gem is available as open source under the terms of the MIT License.