DEV Community

Cover image for Testing Infrastructure with Test Kitchen and Chef
Jennifer Davis for Microsoft Azure

Posted on

Testing Infrastructure with Test Kitchen and Chef

We've selected our favorite tips and tricks created by Michael Crump as well as planned fresh technical content on Azure all April! Miss a day (or more)? Catch up with the series.

Don't have Azure? Grab a free subscription.

Once I've have a Test Kitchen configuration that works I can copy that configuration from project to project modifying what is needed. How do I write tests for the infrastructure that I'm building though?

In this post, I'm going to walk through how to test a sample Node.js project using InSpec, Test Kitchen, and the kitchen-azurerm driver. Everything in this post depends on the work from Testing Infrastructure with Test Kitchen and Chef on Azure so if you are trying to replicate these steps make sure to check out that post first. Additionally, Test Kitchen can be configured to work with a variety of provisioners and drivers, so if you use Puppet or Ansible; for example, you can totally use those instead.

As mentioned in yesterday's post, I've got the Chef Development Kit(ChefDK) installed on my system which includes InSpec. Towards the end of 2018, Chef released the Chef Workstation as a replacement for the Chef Development Kit. If you don't have ChefDK, Chef Workstation is a good starting point with the same software included.

Before I get too into the details, the goal of the article is to show InSpec and not the perfect way to write Chef code, especially for this application. For example, there is a nodejs community cookbook that has a lot more bells and whistles that might provide the necessary functionality for deploying Node.js/npm and managing npm packages. With that said, let's get started!


Revisiting line 14 from the .kitchen.yml file, we can see the configuration verifier.

The verifier is what we use to test the infrastructure configuration that we have defined. In this configuration, it's set to inspec, but we can also use Serverspec or Bats.

Note: It's possible to use InSpec without Test Kitchen, and validate the infrastructure that we've built with Azure Resource Management templates or Terraform as well. Check out all these great InSpec Azure Resources to validate different parts of your infrastructure!

Describing the Infrastructure Testing Process

I'm going to follow the Red-Green-Refactor cycle in developing my infrastructure code. First, I'll write the test that describes a basic feature I want to add. This test will fail because I haven't written any infrastructure code. This is the "red" or failed phase. Then I'll write the infrastructure code that will make the test pass. This is the "green" or passing phase. Then I'll refactor the code I just wrote to make it better.

One critical difference in infrastructure coding versus project code is unit tests are generally the first tests and largest number of tests to write as per the testing pyramid. In infrastructure coding, unit tests are often written incorrectly and test the infrastructure application. For example, writing a test that a package using default package resources gets installed in the right place. That kind of test belongs in a test suite for Chef or Puppet and not within the infrastructure code.

Depending on the type of infrastructure unit you are writing, for example with Chef whether it's a cookbook that is a library cookbook versus a wrapper cookbook, you might want to unit test whether a specific file gets created. That's a subtle difference in testing. Testing whether your infrastructure application does the right thing means you are introducing brittleness into your testing suite which makes testing harder as well as more error prone.

Is there a reason to write unit tests for infrastructure code? Sure! Especially when we are writing complex code that needs to exercise different paths. For unit tests with Chef, I'd use chefspec. For now, I'm starting with integration tests and using InSpec.

Implementing a Test

The first thing I would need to get an Express app running is to make sure I have Node.js and npm installed on my node. I use the command resource to validate that Node.js is installed. The command resource allows me to run a specific command on the instance and validate a set of properties.

Yesterday, when I ran chef generate cookbook test_software_cookbook it set up a bunch of scaffolding for me that includes an integration test at test_software_cookbook/test/integration/default/default_test.rb. I cleaned up the contents and implemented the first test.

I run kitchen converge followed by kitchen verify, and I get my expected "red" output because Node.js isn't installed.

After running kitchen verify I get the exit_status crossed off as I'm failing the test.

Implementing Passing Infrastructure Code

To install Node.js from the Ubuntu provided repositories, I can use the Chef package resource.

Note: In an enterprise environment, I might have my artifacts cached locally so I'd have a different method of defining this code, but it's sufficient for this example.

I run kitchen converge. This time I can see that my node is getting updated and Node.js is getting installed:

       Recipe: test_software_cookbook::default
         * apt_package[nodejs] action install
           - install version 8.10.0~dfsg-2ubuntu0.4 of package nodejs
Enter fullscreen mode Exit fullscreen mode

Now when I run kitchen verify, I get my expected "green" output because Node.js is installed.

After running kitchen verify I get a Test Summary showing in green text 1 successful test and with the exit_status checked off as passing the test.

That's an old version of Node.js, but it's what is available with Ubuntu 18.10 default packages.

To refactor, I use the Chef apt_repository resource to update where I download updated binary distributions from NodeSource.

During the converge I can see the latest Node.js getting installed from NodeSource

Chef converge showing install version 10.15.3-1nodesource1 of package nodejs

I could have started with the InSpec package resource which would allow me to test a specific package and version of Node.js as well. This is extremely useful for example if you want to make sure a minimum version is installed (or a specific version is not installed for example due to a security issue!).

Adding a Required NPM Package

As I continue to write my infrastructure code, I keep following the Red/Green/Refactor cycle, implementing small tests, then adding a feature, and then refactoring. There are InSpec npm resources available beyond just the standard package resources.

Interested in more Chef + Test Kitchen + InSpec + Azure content? Let me know in comments below and I'll add more. There is a lot more areas I could cover, but I'd like to focus on what is helpful.

Cleaning Up My Azure Resources

Always remember to clean up cloud resources you use for development and testing. I clean up my instances with kitchen destroy and that deletes all the resources I created.

$ kitchen destroy
-----> Starting Kitchen (v2.0.1)
-----> Destroying <default-ubuntu-1804>...
       Azure environment: Azure
       Destroying Resource Group: kitchen-default-ubuntu-1804-20190424T071334
       Destroy operation accepted and will continue in the background.
       Finished destroying <default-ubuntu-1804> (0m1.61s).
-----> Kitchen is finished. (0m11.28s)
Enter fullscreen mode Exit fullscreen mode

Additional Resources

We'll be posting articles every day in April, so stay tuned or jump ahead and check out more tips and tricks now.

Top comments (0)