Minitest Chef Handler

Run minitest suites after your Chef recipes to check the status of your system.

Motivation

Working at Engine Yard I have to maintain a quite complicated set of Chef recipes that we use to set up our customers' instances. I need to be sure that everytime someone modifies those recipes, mostly myself, the provisioned services continue working as expected.

There are other solutions that evaluate the configured node after the recipes are loaded without arriving to the converge phase, like ChefSpec or rspec-chef, but I needed something to write integration tests easily. I checked chef-minitest but I'm still amazed by the ugly code that I have to write into the recipes to make it work.

Installation

$ gem install minitest-chef-handler

Usage

Add the report handler to your client.rb or solo.rb file:

require 'minitest-chef-handler'

report_handlers << MiniTest::Chef::Handler.new

Test cases

Write your tests as normal MiniTest cases extending from MiniTest::Chef::TestCase:

class TestNginx < MiniTest::Chef::TestCase
  def test_config_file_exist
    assert File.exist?('/etc/nginx.conf')
  end
end

You still have access to Chef's run_status, node and run_context from your tests:

class TestNginx < MiniTest::Chef::TestCase
  def test_succeed
    assert run_status.success?
  end
end

Spec cases

Wrap your descriptions with a class extending from MiniTest::Chef::Spec:

class NginxSpec < MiniTest::Chef::Spec
  describe 'configuration' do
    it 'creates nginx.conf'
  end
end

Use the prefix recipe:: in your descriptions:

describe "recipe::nginx::configuration" do
  it 'creates nginx.conf'
end

Or use describe_recipe to define your specs:

describe_recipe "nginx::configuration" do
  it 'creates nginx.conf'
end

You still have access to Chef's run_status, node and run_context from your specs:

describe_recipe 'nginx:configuration' do
  it 'installs version 1.0.15' do
    node[:nginx][:version].should == '1.0.15'
  end
end

Custom assertions

By including MiniTest::Chef::Resources and MiniTest::Chef::Assertions you can also make assertions like these:

file("/etc/fstab").must_have(:mode, "644")
package("less").must_be_installed
service("chef-client").must_be_running

The resources supported are: cron, directory, file, group, ifconfig, link, mount, package, service and user.

For example usage see the tests under the examples/spec_examples directory.

Further configuration

These are the options the handler accepts:

  • :path => where your test files are, './test/test_*.rb' by default
  • :filter => filter test names on pattern
  • :seed => set random seed
  • :verbose => show progress processing files.

Example:

handler = MiniTest::Chef::Handler.new({
  :path    => './cookbooks/test/*_test.rb',
  :filter  => 'foo',
  :seed    => srand,
  :verbose => true})

report_handlers << handler

Automatic tests detection

MiniTest-chef-hander collects test paths based in the recipes ran. It loads the tests based in the name of the cookbook and the name of the recipe. The tests must be under the cookbooks directory.

Examples:

If the seen recipes includes the recipe "foo" we try to load tests from:

cookbooks/foo/tests/default_test.rb
cookbooks/foo/tests/default/*_test.rb

cookbooks/foo/specs/default_spec.rb
cookbooks/foo/specs/default/*_spec.rb

If the seen recipes includes the recipe "foo::install" we try to load tests from:

cookbooks/foo/tests/install_test.rb
cookbooks/foo/tests/install/*_test.rb

cookbooks/foo/specs/install_spec.rb
cookbooks/foo/specs/install/*_spec.rb

Automatic chef failure

If the tests detect any failure, the handler raises an error to abort the Chef execution. This error can be captured by any other exception handler and be treated like any other error in the Chef execution.

Chef server distribution

The instructions abow have described how to use it in a Chef solo installation. If you want to distribute the handler to your Chef server check either the chef_handler cookbooks in the examples or minitest-handler-cookbook.

Copyright (c) 2012 David Calavera. See LICENSE for details.