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!
- Chef Development Kit(ChefDK)
- Service Principal
- ~/.azure/credentials configured with your Service Principal configuration
Revisiting line 14 from the .kitchen.yml file, we can see the configuration verifier.
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!
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.
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.
kitchen converge followed by
kitchen verify, and I get my expected "red" output because Node.js isn't installed.
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.
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
Now when I run
kitchen verify, I get my expected "green" output because Node.js is installed.
That's an old version of Node.js, but it's what is available with Ubuntu 18.10 default packages.
During the converge I can see the latest Node.js getting installed from NodeSource
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!).
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.
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)
- Learn about Infrastructure as Code
- Test Driven Development with InSpec
- Use InSpec for testing your Azure Infrastructure
We'll be posting articles every day in April, so stay tuned or jump ahead and check out more tips and tricks now.