rspec-puppet

RSpec tests for your Puppet manifests

View the Project on GitHub rodjek/rspec-puppet

Home

Tutorial

Setup

Matchers

Changelog

Matchers

Classes, Defined Types and Hosts

compile

This is the most basic test you can do on your manifest

it { should compile }

This matcher also has an optional method that you may want to use, with_all_deps. This will check if all the resources you have specified relationships to (with require, notify, subscribe, before or the chaining arrows) also exist in the catalogue.

it { should compile.with_all_deps }

You’ll probably want to use this when testing host or role catalogues, but perhaps not when testing individual modules that might reference other modules.

contain_*

In order to test that your manifest contains a particular Puppet resource, you should use the generic contain_<resource> class.

it { should contain_service('apache') }

If the resource type you’re testing for contains :: in it, replace the :: with __ (two underscores). For example, to test that your manifest contains apache::vhost you would do

it { should contain_apache__vhost('my awesome vhost') }

You can also test for the presence of Puppet classes in the same manner.

it { should contain_class('apache') }

with_* and without_*

Further to this, you can test for the presence or absence of parameters on these resources by chaining any number of .with_<parameter> or .without_<parameter> methods onto the end of your test. These methods can either take an exact value, a regular expression or a Ruby Proc.

it { should contain_service('mysql-server').with_ensure('present') }
it { should contain_file('/etc/logrotate.d/apache').with_content(/compress/) }
it { should contain_file('/etc/logrotate.d/apache').without_owner('root') }

only_with_*

If you want to test the presence of an exact set of parameters on your resources, you can do so by chaining the .only_with_<parameter> methods. These methods also take an exact value or a regular expression.

it { should contain_service('httpd').only_with_ensure('running') }

with and without

This can become very verbose when you’re testing for multiple parameters, so you can also chain .with and .without methods on to the end of your tests and pass it a hash of parameters.

it do
  should contain_service('apache').with(
    'ensure'     => 'running',
    'enable'     => 'true',
    'hasrestart' => 'true',
  )
end

only_with

The chaining also works with the .only_with method, by passing a hash of parameters. These will be the exact set of parameters the catalogue should contain for that resource or class.

it do
  should contain_user('luke').only_with(
    'ensure' => 'present',
    'uid'    => '501',
  )
end

have_resource_count

To test for an exact number of resources in the manifest, you can use the have_resource_count matcher.

it { should have_resource_count(2) }

have_class_count

It is also possible to test the number of classes in a manifest. Use the have_class_count matcher for this.

it { should have_class_count(1) }

have_*_resource_count

To test the number of resources of a specific type in the manifest, the have_<resource type>_resource_count matcher is available. This works for both native and defined types.

If the resource type you’re testing for contains :: in it, replace the :: with __ (two underscores). For example, to test that your manifest contains a single logrotate::rule resource you would do

it { should have_logrotate__rule_resource_count(1) }

NOTE: when testing a class, the catalogue generated will always contain at least one class, the class under test. The same holds for defined types, the catalogue generated when testing a defined type will have at least one resource (the defined type itself).

Relationship matchers

The following methods will allow you to test the relationships between the resources in your catalogue, regardless of how the relationship is defined. This means that it doesn’t matter if you prefer to define your relationships with the metaparameters (require, before, notify and subscribe) or the chaining arrows (->, ~>, <- and <~), they’re all tested the same.

it { should contain_file('foo').that_requires(File[bar]) }
it { should contain_file('foo').that_comes_before(File[bar]) }
it { should contain_file('foo').that_notifies(File[bar]) }
it { should contain_file('foo').that_subscribes_to(File[bar]) }

You can also test the reverse direction of the relationship, so if you have the following bit of Puppet code

notify { 'foo': }
notify { 'bar':
  before => Notify['foo'],
}

You can test that Notify[bar] comes before Notify[foo]

it { should contain_notify('bar').that_comes_before('Notify[foo]') }

Or, you can test that Notify[foo] requires Notify[bar]

it { should contain_notify('foo').that_requires('Notify[bar]') }

Testing for errors

Sometimes you want to test that a particular situation will cause Puppet to raise an error, such as paramater validation. You can accomplish this using a combination of rspec-puppet and RSpec matchers.

describe 'my::type' do
  context 'with foo => true' do
    let(:params) { {:foo => true} }

    it { should compile }
  end

  context 'with foo => bar' do
    let(:params) { {:foo => 'bar'} }

    it do
      expect {
        should compile
      }.to raise_error(Puppet::Error, /foo must be a boolean/)
    end
  end
end

Functions

run

In order to test that a Puppet function works correctly, you should use the run matcher.

it { should run.with_params('foo').and_return('bar') }

You can also test how your Puppet function fails under certain conditions using the and_raise_error.

You can test the message raised with the exception by passing a string or regular expression to and_raise_error

it { should run.with_params().and_raise_error(/incorrect number of arguments) }

Or, you can test for just the type of exception raised

it { should run.with_params().and_raise_error(ArgumentError) }

If you want to be thorough, you can also test for the exception type and the message

it { should run.with_params().and_raise_error(ArgumentError, /number of arguments/) }