Testing your cookbooks with unit tests will help you not to break existing functionality. But how can you be sure that the recipe will successfully run on production node? The only answer to that is running this recipe on some other/similar machine and checking if everything is actually configured the way it must be. Don’t worry we will not need some additional hardware for testing cookbooks, it can comfortably be done in virtual machine or container.

We will use Lxd container and the image we created in previous post, inspec testing library and Test Kitchen to glue those things together. Test Kitchen web page has a good tutorial on how to start writing the tests, you should definitely look at it.

We will do a couple of things differently:

  • we will have .kitchen.yml file not inside every cookbook, but in the root of our project, as our cookbbooks are depending on each other. Test Kitchen will not be able to find other cookbooks, if .kitchen.yml is inside of some cookbook.
  • we will use inspec to run tests instead of bats.

I couldn’t make existing Test Kitchen lxd drivers (lxd_cli and lxd_api), so I created my own: lxd. Create a following .kitchen.yml in the root of your project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
---
driver:
  name: lxd # we will use kitchen-lxd gem. You have to install it on your own.

transport:
  ssh_key: ~/.ssh/id_rsa # path to your ssh key

provisioner:
  name: chef_solo
  require_chef_omnibus: false # our lxd image has already chef installed
  chef_solo_path: chef-solo # our chef is in PATH

platforms:
  - name: kitchen-xenial64 # lxd image name

verifier:
  name: inspec
  format: junit
  output: inspec_output.xml

suites:
  - name: ruby-rake
    run_list:
      - recipe[ruby::rake]
    verifier:
      inspec_test:
        - test/recipes
    attributes:

We are testing ruby::rake recipe, which just installs a particular version on rake gem. Now we have to test, if we can run rake and it has the right version. Create file at test/integration/ruby-rake/inspec/ruby-rake_spec.rb:

1
2
3
4
5
6
7
8
9
control "ruby-rake" do                                # A unique ID for this control
  impact 1.0                                          # Just how critical is
  title "Recipe ruby::rake"                           # Readable by a human
  desc "Text should include the words 'hello world'." # Optional description

  describe command('rake -V') do                      # The actual test
    its('stdout') { should match 'rake, version 10.5.0' }
  end
end

Now when we run kitchen test:

  • an Lxd container will be launched from kitchen-xenial64 image
  • chef-solo will be run inside the container with ‘ruby:rake’ in run list
  • a test will be run, checking the aoutput of rake -V command
  • test results will be written into inspec_output.xml file
  • the container will be destroyed

To add more tests, just add more suites to .kitchen.yml and more tests to test/integration/ directory.