Getting Started

Seth Vargo edited this page Jan 17, 2014 · 11 revisions
Clone this wiki locally

Test Kitchen is Integration Testing for your Chef Cookbooks.


Pre-requisite software:

To get started, you'll need to add Test Kitchen to your Gemfile inside your Chef Cookbook:

source ''
gem 'test-kitchen', '~> 1.0'

group :integration do
  gem 'kitchen-vagrant', '~> 0.11'

kitchen-vagrant is a "Test Kitchen driver" - it tells test-kitchen how to interact with an appliance (machine), such as Vagrant, EC2, Rackspace, etc.

And run the bundle command to install:

$ bundle install

To verify Test Kitchen is installed, run the kitchen help command:

$ bundle exec kitchen help

You should see something like:

  kitchen console                          # Kitchen Console!
  kitchen converge [(all|<REGEX>)] [opts]  # Converge one or more instances
  kitchen create [(all|<REGEX>)] [opts]    # Create one or more instances
  kitchen destroy [(all|<REGEX>)] [opts]   # Destroy one or more instances
  kitchen driver                           # Driver subcommands
  kitchen driver create [NAME]             # Create a new Kitchen Driver gem project
  kitchen driver discover                  # Discover Test Kitchen drivers published on RubyGems
  kitchen driver help [COMMAND]            # Describe subcommands or one specific subcommand
  kitchen help [COMMAND]                   # Describe available commands or one specific command
  kitchen init                             # Adds some configuration to your cookbook so Kitchen can rock
  kitchen list [(all|<REGEX>)]             # List all instances
  kitchen login (['REGEX']|[INSTANCE])     # Log in to one instance
  kitchen setup [(all|<REGEX>)] [opts]     # Setup one or more instances
  kitchen test [all|<REGEX>)] [opts]       # Test one or more instances
  kitchen verify [(all|<REGEX>)] [opts]    # Verify one or more instances
  kitchen version                          # Print Kitchen's version information


Test Kitchen stores all of it's information about state in a special directory .kitchen at the root of your cookbook. Test Kitchen also uses a special configuration file, .kitchen.yml, that tells Test Kitchen how and what to test. Create a .kitchen.yml at the root of your cookbook:

driver_plugin: vagrant
  require_chef_omnibus: true

  - name: ubuntu-12.04
  - name: centos-6.4

  - name: default
      - recipe[my_cookbook]
    attributes: {}

Note, in this example, we are using Vagrant and the Opscode base boxes. There are other drivers (see additional wiki pages), but Vagrant is the easiest to get started.

If you haven't downloaded these base boxes before, Test Kitchen will automatically download them for you.

Warning: Some of these base boxes are > 600MB. Make sure you are on a stable Internet connection.

By default, Test Kitchen will look in the test/integration directory for your test files. Inside there will be a folder, inside the folder a busser, and inside the busser, the tests:

  |_ do_something <-- suite name (more on this later)
    |_ bats <-- busser (AKA the "tester")
      |_ my_test.bats <-- a test

And here's what a simple test looks like:

@test "the thing is done correctly" {
  grep "some content" /var/my_file

There are multiple bussers available including busser-bats (default), busser-bash, busser-minitest. In short, busser is a test setup and execution framework designed to work on remote nodes whose system dependencies cannot be relied upon, except for an Omnibus installation of Chef.

Back in our .kitchen.yml, we need to tell Chef about our test "suites". A suite is a run_list and attributes collection and corresponds to the name of the directory under test/integration as shown above. If I named my suite "billyjoe", it would live in test/integration/billyjoe (note this is independent of the run_list specified). Here's an example suite:

  - name: my_suite
      - recipe[my_cookbook::do_something]

Notice that the run_list is an array, just like in Chef. You can have multiple items in the run_list, should you wish, for example:

  - name: my_suite
      - recipe[my_cookbook::default]
      - recipe[my_cookbook::do_something]

You can also specify node attributes via the attributes hash (optional):

  - name: my_suite
      - recipe[my_cookbook::do_something]
    attributes: { foo: "bar" }

We can now execute the kitchen test:

$ bundle exec kitchen test

This will run our tests against the platforms defined in our .kitchen.yml (ubuntu-12.04 and centos-6.4):

Real Example

Let's look at a real example: tmux. Imagine the following hypothetical cookbook:

package 'tmux'

template '/etc/tmux.conf' do
  source 'tmux.conf.erb'
  mode   '0644'

And an associated tmux.conf.erb template (not shown).

Add test-kitchen and kitchen-vagrant to your Gemfile, as from above:

source ''
gem 'test-kitchen', '~> 1.0'

group :integration do
  gem 'kitchen-vagrant', '~> 0.11'

And run the bundle command to install:

$ bundle install

Add a .kitchen.yml to test on Ubuntu 12.04:

driver_plugin: vagrant
  require_chef_omnibus: true

  - name: ubuntu-12.04

  - name: default
      - recipe[tmux::default]

And we can write our first integration test (we will just use bats because it's easiest):

# test/integration/default/bats/verify_installed.bats

@test "tmux is installed and in the path" {
  which tmux

@test "tmux configuration exists" {
  cat /etc/tmux.conf | grep "map" # this could be a more complex test

As a reminder, the format for the path to a test is:


Now we can run the kitchen test command:

$ bundle exec kitchen test

This will (download), spin up, converge, and run the bats tests on a Vagrant virtual machine. If they pass, Test Kitchen will automatically destroy that virtual machine for us.

$ bundle exec kitchen test
-----> Starting Kitchen (v1.0.0)
-----> Cleaning up any prior instances of <default-ubuntu-1204>
-----> Destroying <default-ubuntu-1204>
       [kitchen::driver::vagrant command] BEGIN (vagrant destroy -f)
       [default] Forcing shutdown of VM...
       [default] Destroying VM and associated drives...
       [kitchen::driver::vagrant command] END (0m3.59s)
       Vagrant instance <default-ubuntu-1204> destroyed.
       Finished destroying <default-ubuntu-1204> (0m3.92s).
-----> Testing <default-ubuntu-1204>
-----> Creating <default-ubuntu-1204>
       [kitchen::driver::vagrant command] BEGIN (vagrant up --no-provision)
       Bringing machine 'default' up with 'virtualbox' provider...
       [default] Importing base box 'opscode-ubuntu-12.04'...
       [default] Matching MAC address for NAT networking...
       [default] Setting the name of the VM...
       [default] Clearing any previously set forwarded ports...
       [default] Creating shared folders metadata...
       [default] Clearing any previously set network interfaces...
       [default] Preparing network interfaces based on configuration...
       [default] Forwarding ports...
       [default] -- 22 => 2222 (adapter 1)
       [default] Running any VM customizations...
       [default] Booting VM...
       [default] Waiting for VM to boot. This can take a few minutes.
       [default] VM booted and ready for use!
       [default] Setting hostname...
       [default] Configuring and enabling network interfaces...
       [default] Mounting shared folders...
       [default] -- /vagrant
       [kitchen::driver::vagrant command] END (0m33.01s)
       [kitchen::driver::vagrant command] BEGIN (vagrant ssh-config)
       [kitchen::driver::vagrant command] END (0m1.76s)
       Vagrant instance <default-ubuntu-1204> created.
       Finished creating <default-ubuntu-1204> (0m36.85s).
-----> Converging <default-ubuntu-1204>
       Uploaded tmux/metadata.rb (259 bytes)
       Uploaded tmux/ (914 bytes)
       Uploaded tmux/recipes/default.rb (680 bytes)
       Uploaded tmux/templates/default/tmux.conf.erb (431 bytes)
Starting Chef Client, version 11.6.0      
[2013-05-15T19:07:52+00:00] INFO: *** Chef 11.6.0 ***       
[2013-05-15T19:07:52+00:00] INFO: Setting the run_list to ["tmux::default"] from JSON       
[2013-05-15T19:07:52+00:00] INFO: Run List is [recipe[tmux::default]]       
[2013-05-15T19:07:52+00:00] INFO: Run List expands to [tmux::default]       
[2013-05-15T19:07:52+00:00] INFO: Starting Chef Run for default-ubuntu-1204       
[2013-05-15T19:07:52+00:00] INFO: Running start handlers       
[2013-05-15T19:07:52+00:00] INFO: Start handlers complete.       
Compiling Cookbooks...       
Converging 2 resources       
Recipe: tmux::default       
  * package[tmux] action install[2013-05-15T19:07:52+00:00] INFO: Processing package[tmux] action install (tmux::default line 19)       

    - install version 1.6-1ubuntu1 of package tmux       

  * template[/etc/tmux.conf] action create[2013-05-15T19:07:56+00:00] INFO: Processing template[/etc/tmux.conf] action create (tmux::default line 21)       
[2013-05-15T19:07:56+00:00] INFO: template[/etc/tmux.conf] updated content       
[2013-05-15T19:07:56+00:00] INFO: template[/etc/tmux.conf] mode changed to 644       

    - create template[/etc/tmux.conf]       
        --- /tmp/chef-tempfile20130515-1047-1phg51c 2013-05-15 19:07:56.797504430 +0000       
        +++ /tmp/chef-rendered-template20130515-1047-1f3d3vd  2013-05-15 19:07:56.797504430 +0000       
        @@ -0,0 +1,23 @@       
        +# Use a better prefix:       
        +set -g prefix C-a       
        +unbind C-b       

        +# Change the default delay:
        +set -sg escape-time 1
        +# Set the window and panes index
        +set -g base-index 1
        +setw -g pane-base-index 1

        +# Send prefix to ohter apps:
        +bind C-a send-prefix
        +# Split windows with more logical keys

        +bind | split-window -h

        +bind - split-window -v

        +# Remap movement keys
        +bind h select-pane -L
        +bind j select-pane -D

        +bind k select-pane -U
        +bind l select-pane -R

[2013-05-15T19:07:56+00:00] INFO: Chef Run complete in 3.930695673 seconds       
[2013-05-15T19:07:56+00:00] INFO: Running report handlers       
       [2013-05-15T19:07:56+00:00] INFO: Report handlers complete
       Chef Client finished, 2 resources updated

       Finished converging <default-ubuntu-1204> (0m5.50s).
-----> Setting up <default-ubuntu-1204>
Fetching: thor-0.18.1.gem (100%)       
Fetching: busser-0.4.1.gem (100%)       
Successfully installed thor-0.18.1       
Successfully installed busser-0.4.1       
2 gems installed       
-----> Setting up Busser       
       Creating BUSSER_ROOT in /opt/busser       
       Creating busser binstub       
       Plugin bats installed (version 0.1.0)       
-----> Running postinstall for bats plugin       
      create  /tmp/bats20130515-1418-a5fn31/bats       
      create  /tmp/bats20130515-1418-a5fn31/bats.tar.gz       
Installed Bats to /opt/busser/vendor/bats/bin/bats       
      remove  /tmp/bats20130515-1418-a5fn31       
       Finished setting up <default-ubuntu-1204> (0m6.55s).
-----> Verifying <default-ubuntu-1204>
       Suite path directory /opt/busser/suites does not exist, skipping.       
Uploading /opt/busser/suites/bats/verify_installed.bats (mode=0644)       
-----> Running bats test suite       
ok 1 tmux is installed and in the path       
ok 2 tmux configuration exists       
       Finished verifying <default-ubuntu-1204> (0m1.43s).
-----> Destroying <default-ubuntu-1204>
       [kitchen::driver::vagrant command] BEGIN (vagrant destroy -f)
       [default] Forcing shutdown of VM...
       [default] Destroying VM and associated drives...
       [kitchen::driver::vagrant command] END (0m3.55s)
       Vagrant instance <default-ubuntu-1204> destroyed.
       Finished destroying <default-ubuntu-1204> (0m3.90s).
       Finished testing <default-ubuntu-1204> (0m58.16s).
-----> Kitchen is finished. (0m59.76s)

We just converged and tested a node in less than 1 minute! Happy testing!