2011-11-01 03:40:31 +00:00
|
|
|
# RSpec tests for your Puppet manifests & modules
|
2011-07-17 01:05:01 +00:00
|
|
|
|
2011-07-18 23:32:20 +00:00
|
|
|
## Installation
|
|
|
|
|
2011-07-19 02:18:08 +00:00
|
|
|
gem install rspec-puppet
|
|
|
|
|
|
|
|
## Naming conventions
|
|
|
|
|
|
|
|
For clarity and consistency, I recommend that you use the following directory
|
|
|
|
structure and naming convention.
|
|
|
|
|
|
|
|
module
|
|
|
|
|
|
|
|
|
+-- manifests
|
|
|
|
|
|
|
|
|
+-- lib
|
|
|
|
|
|
|
|
|
+-- spec
|
|
|
|
|
|
|
|
|
+-- spec_helper.rb
|
|
|
|
|
|
|
|
|
+-- classes
|
|
|
|
| |
|
|
|
|
| +-- <class_name>_spec.rb
|
|
|
|
|
|
|
|
|
+-- defines
|
2011-11-01 03:40:31 +00:00
|
|
|
| |
|
|
|
|
| +-- <define_name>_spec.rb
|
|
|
|
|
|
|
|
|
+-- functions
|
2012-02-15 05:33:48 +00:00
|
|
|
| |
|
|
|
|
| +-- <function_name>_spec.rb
|
|
|
|
|
|
|
|
|
+-- hosts
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
2012-02-15 05:33:48 +00:00
|
|
|
+-- <host_name>_spec.rb
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
## Example groups
|
|
|
|
|
|
|
|
If you use the above directory structure, your examples will automatically be
|
2011-11-01 03:40:31 +00:00
|
|
|
placed in the correct groups and have access to the custom matchers. *If you
|
|
|
|
choose not to*, you can force the examples into the required groups as follows.
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
describe 'myclass', :type => :class do
|
|
|
|
...
|
|
|
|
end
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
describe 'mydefine', :type => :define do
|
|
|
|
...
|
|
|
|
end
|
2011-11-01 03:40:31 +00:00
|
|
|
|
|
|
|
describe 'myfunction', :type => :puppet_function do
|
|
|
|
...
|
|
|
|
end
|
2012-02-15 05:33:48 +00:00
|
|
|
|
|
|
|
describe 'myhost.example.com', :type => :host do
|
|
|
|
...
|
|
|
|
end
|
2011-08-11 19:56:41 +00:00
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
## Defined Types & Classes
|
|
|
|
|
|
|
|
### Matchers
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Checking if a class has been included
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
You can test if a class has been included in the catalogue with the
|
|
|
|
`include_class` matcher. It takes the class name as a string as its only
|
|
|
|
argument
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
it { should include_class('foo') }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 08:20:38 +00:00
|
|
|
#### Checking if a resource exists
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
You can test if a resource exists in the catalogue with the generic
|
2011-08-11 19:56:41 +00:00
|
|
|
`contain_<resource type>` matcher.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it { should contain_augeas('bleh') }
|
|
|
|
```
|
|
|
|
|
|
|
|
If your resource type includes :: (e.g.
|
2011-08-11 00:35:56 +00:00
|
|
|
`foo::bar` simply replace the :: with __ (two underscores).
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
it { should contain_foo__bar('baz') }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
You can further test the parameters that have been passed to the resources with
|
|
|
|
the generic `with_<parameter>` chains.
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
it { should contain_package('mysql-server').with_ensure('present') }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-12-29 23:37:24 +00:00
|
|
|
You can use the `with` method to verify the value of multiple parameters.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it do should contain_service('keystone').with(
|
|
|
|
'ensure' => 'running',
|
|
|
|
'enable' => 'true',
|
|
|
|
'hasstatus' => 'true',
|
|
|
|
'hasrestart' => 'true'
|
|
|
|
) end
|
|
|
|
```
|
|
|
|
|
2011-08-29 02:18:22 +00:00
|
|
|
You can also test that specific parameters have been left undefined with the
|
|
|
|
generic `without_<parameter>` chains.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it { should contain_file('/foo/bar').without_mode }
|
|
|
|
```
|
|
|
|
|
2011-12-29 23:41:29 +00:00
|
|
|
You can use the without method to verify that a list of parameters have not been
|
|
|
|
defined
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it { should contain_service('keystone').without(
|
|
|
|
['restart', 'status']
|
|
|
|
)}
|
|
|
|
```
|
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
### Writing tests
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Basic test structure
|
2011-07-17 01:05:01 +00:00
|
|
|
|
2011-07-17 01:08:01 +00:00
|
|
|
To test that
|
|
|
|
|
|
|
|
sysctl { 'baz'
|
|
|
|
value => 'foo',
|
|
|
|
}
|
|
|
|
|
|
|
|
Will cause the following resource to be in included in catalogue for a host
|
|
|
|
|
|
|
|
exec { 'sysctl/reload':
|
|
|
|
command => '/sbin/sysctl -p /etc/sysctl.conf',
|
|
|
|
}
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
We can write the following testcase (in `spec/defines/sysctl_spec.rb`)
|
2011-07-17 01:08:01 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
describe 'sysctl' do
|
|
|
|
let(:title) { 'baz' }
|
|
|
|
let(:params) { { :value => 'foo' } }
|
2011-07-17 01:05:01 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
it { should contain_exec('sysctl/reload').with_command("/sbin/sysctl -p /etc/sysctl.conf") }
|
|
|
|
end
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the title of a resource
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
let(:title) { 'foo' }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the parameters to pass to a resources or parametised class
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
let(:params) { {:ensure => 'present', ...} }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the FQDN of the test node
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
If the manifest you're testing expects to run on host with a particular name,
|
|
|
|
you can specify this as follows
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
let(:node) { 'testhost.example.com' }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the facts that should be available to your manifest
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
By default, the test environment contains no facts for your manifest to use.
|
|
|
|
You can set them with a hash
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
let(:facts) { {:operatingsystem => 'Debian', :kernel => 'Linux', ...} }
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the path to find your modules
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
I recommend setting a default module path by adding the following code to your
|
|
|
|
`spec_helper.rb`
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
RSpec.configure do |c|
|
|
|
|
c.module_path = '/path/to/your/module/dir'
|
|
|
|
end
|
|
|
|
```
|
2011-07-19 02:18:08 +00:00
|
|
|
|
|
|
|
However, if you want to specify it in each example, you can do so
|
|
|
|
|
2011-08-11 19:56:41 +00:00
|
|
|
```ruby
|
|
|
|
let(:module_path) { '/path/to/your/module/dir' }
|
|
|
|
```
|
2011-11-01 03:40:31 +00:00
|
|
|
|
|
|
|
## Functions
|
|
|
|
|
|
|
|
### Matchers
|
|
|
|
|
|
|
|
All of the standard RSpec matchers are available for you to use when testing
|
|
|
|
Puppet functions.
|
|
|
|
|
2011-11-01 08:03:35 +00:00
|
|
|
```ruby
|
|
|
|
it 'should be able to do something' do
|
|
|
|
subject.call('foo') == 'bar'
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
|
|
|
For your convenience though, a `run` matcher exists to provide easier to
|
|
|
|
understand test cases.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it { should run.with_params('foo').and_return('bar') }
|
|
|
|
```
|
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
### Writing tests
|
|
|
|
|
|
|
|
#### Basic test structure
|
|
|
|
|
2011-11-01 08:03:35 +00:00
|
|
|
```ruby
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
describe '<function name>' do
|
|
|
|
...
|
|
|
|
end
|
|
|
|
```
|
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the name of the function to test
|
|
|
|
|
2011-11-01 08:03:35 +00:00
|
|
|
The name of the function must be provided in the top level description, e.g.
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
describe 'split' do
|
|
|
|
```
|
|
|
|
|
2011-11-01 03:40:31 +00:00
|
|
|
#### Specifying the arguments to pass to the function
|
|
|
|
|
2011-11-01 08:03:35 +00:00
|
|
|
You can specify the arguments to pass to your function during the test(s) using
|
|
|
|
either the `with_params` chain method in the `run` matcher
|
2011-11-01 03:40:31 +00:00
|
|
|
|
|
|
|
```ruby
|
2011-11-01 08:03:35 +00:00
|
|
|
it { should run.with_params('foo', 'bar', ['baz']) }
|
2011-11-01 03:40:31 +00:00
|
|
|
```
|
|
|
|
|
2011-11-01 08:03:35 +00:00
|
|
|
Or by using the `call` method on the subject directly
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it 'something' do
|
|
|
|
subject.call('foo', 'bar', ['baz'])
|
|
|
|
end
|
|
|
|
```
|
2011-11-01 03:40:31 +00:00
|
|
|
|
|
|
|
#### Testing the results of the function
|
|
|
|
|
2011-11-01 08:03:35 +00:00
|
|
|
You can test the result of a function (if it produces one) using either the
|
|
|
|
`and_returns` chain method in the `run` matcher
|
2011-11-01 03:40:31 +00:00
|
|
|
|
|
|
|
```ruby
|
2011-11-01 08:03:35 +00:00
|
|
|
it { should run.with_params('foo').and_return('bar') }
|
|
|
|
```
|
|
|
|
|
|
|
|
Or by using any of the existing RSpec matchers on the subject directly
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it 'something' do
|
|
|
|
subject.call('foo') == 'bar'
|
|
|
|
subject.call('baz').should be_an Array
|
2011-11-01 03:40:31 +00:00
|
|
|
end
|
|
|
|
```
|
2011-11-01 08:03:35 +00:00
|
|
|
|
|
|
|
#### Testing the errors thrown by the function
|
|
|
|
|
|
|
|
You can test whether the function throws an exception using either the
|
|
|
|
`and_raises_error` chain method in the `run` matcher
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it { should run.with_params('a', 'b').and_raise_error(Puppet::ParseError) }
|
|
|
|
it { should_not run.with_params('a').and_raise_error(Puppet::ParseError) }
|
|
|
|
```
|
|
|
|
|
|
|
|
Or by using the existing `raises_error` RSpec matcher
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
it 'something' do
|
|
|
|
expect { subject.call('a', 'b') }.should raise_error(Puppet::ParseError)
|
|
|
|
expect { subject.call('a') }.should_not raise_error(Puppet::ParseError)
|
2011-11-01 08:05:41 +00:00
|
|
|
end
|
2011-11-01 08:03:35 +00:00
|
|
|
```
|