Overview on how to use RSpec-Puppet.
Per Tim Sharpe, "RSpec-Puppet tests are there to test the behaviour of Puppet when it compiles your manifests into a catalogue of Puppet resources."
Setup module environment for testing
- Caveats
- Parameter Injection
- Fact Injection
- Hiera inlusion
- Resource matcher
- Relationship matcher
- Additional matchers
- Simple compilation test
##TODO
This is how to install RSpec-Puppet and what is needed for this install.
For starters, a Linux virutal machine is necessary.
Must be installed prior to installing RSpec-Puppet:
- PE 3.2.1 (Agent)
- git
- gcc
- gcc-c++
- libxml2-devel (for Nokogiri, an often used Ruby parser with spec tests)
- libxslt-devel (for Nokogiri, an often used Ruby parser with spec tests)
Nice if installed:
- vim
Must be installed prior to installing RSpec-Puppet
- bundler (
/opt/puppet/bin/gem install bundler) - puppetlabs_spec_helper (
/opt/puppet/bin gem install puppetlabs_spec_helper)
Nice if you want to take advantage of stylistic linting for Puppet manfiests
- puppet-lint (
/opt/puppet/bin/gem install puppet-lint)
On the command line:
/opt/puppet/bin/gem install rspec-puppet
To initialize the spec test scaffolding inside the root directory of a module:
cd /root/of/module
/opt/puppet/bin/rspec-puppet-init
Create the following file in the root of the modules directory:
.fixture.yml
Here is an example of how the puppetlabs-apache module includes other modules as dependencies or fixtures:
fixtures:
repositories:
stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git"
concat: "git://github.com/puppetlabs/puppetlabs-concat.git"
symlinks:
apache: "#{source_dir}"
If not auto-populated, add the following to your spec/spec_helper.rb file:
require 'rspec-puppet'
fixture_path = File.expand_path(File.join(__FILE__, '..', '..', 'fixtures'))
RSpec.configure do |c|
c.module_path = File.join(fixture_path, 'modules')
c.manifest_dir = File.join(fixture_path, 'manifests')
end
If not auto-populated, create a rake task to run all tests called Rakefile in the root directory of the module:
require 'rake'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/*/*_spec.rb'
end
If you want to include puppet-lint to perform automatic lint testing of your manifests, add this require line at the top of your Rakefile:
require 'puppet-lint/tasks/puppet-lint'
If you choose to add this linter, be sure to complete the install of puppet-lint
The Gemfile contains all necessary gems required for ruby to be able to run your test(s). A Gemfile is prepoulated with items similar to this when you use the scafolding method:
source ENV['GEM_SOURCE'] || "https://rubygems.org"
group :development, :test do
gem 'rake', :require => false
gem 'rspec-puppet', :require => false
gem 'puppetlabs_spec_helper', :require => false
gem 'serverspec', :require => false
gem 'puppet-lint', :require => false
gem 'beaker', :require => false
gem 'beaker-rspec', :require => false
gem 'pry', :require => false
gem 'simplecov', :require => false
end
if facterversion = ENV['FACTER_GEM_VERSION']
gem 'facter', facterversion, :require => false
else
gem 'facter', :require => false
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end
# vim:ft=ruby
Insure all of the necessary gems are installed, by running the following command in the root directory of the module:
/opt/puppet/bin/bundle install
Additional Gems that might be of help in spec testing might be if you wanted to extend RSpec-Puppet with Pry in order to gain the advantages of this IRB alternative to do deep dive debugging.
Remmeber when naming test files to end them with this suffix: _spec.rb
Class tests should exist in: ./spec/classes
Defined type classes should exist in: ./spec/defines
Host tests should exist in: ./spec/hosts
Function tests should exist in: ./spec/functions
An example of how to inject parameter data into your test (at minimum, within the describe block):
let(:params) {
{:foo => 'bar', :baz => 'gronk'}
}
In order to include your Hiera hierachy into use with your RSpec-Puppet tests, the following examples would be entries in a particular spec test:
let(:hiera_config) { 'spec/fixtures/hiera/hiera.yaml' }
hiera = Hiera.new(:config => 'spec/fixtures/hiera/hiera.yaml')
and the following, as example, would be entered into the spec/fixtures/hiera/hiera.yaml file:
---
:backends:
- yaml
:hierarchy:
- test
:yaml:
:datadir: 'spec/fixtures/hiera'
and then ultimately you would have data in spec/fixtures/hiera/test.yaml, in this instance:
---
ntpserver: ['ntp1.domain.com','ntpXX.domain.com']
user:
oneuser:
shell: '/bin/bash'
twouser:
shell: '/sbin/nologin'
Other ways to handle this would be to symlink your Hiera hierarchy into the spec test, or just copy the entire hierarchy into the test.
By default, the test environment contains only the
let(:facts) {
{:operatingsystem => 'CentOS', :ipaddress => '192.168.0.1'}
}
An example of a simple compilation test would look something like this:
require 'spec_helper'
describe 'puppet-tas' do
it { should compile }
end
An example of a resource block to be contained within a describe block to test the Apache service:
it do
should contain_service('apache').with(
'ensure' => 'running',
'enable' => 'true',
'hasrestart' => 'true',
)
end
An example of how to describe the require,before,notify, and subscribe relationships between resources in RSpec-Puppet:
it {
should contain_service('sshd').that_requires('File[/etc/ssh/sshd_config]')
}
it {
should contain_file('/etc/ssh/sshd_config').that_comes_before('Service[sshd]')
}
it {
should contain_file('/etc/ssh/sshd_config').that_notifies('Service[sshd]')
}
it {
should contain_service('sshd').that_subscribes_to('File[/etc/ssh/sshd_config]')
}
If you want to explore more matchers, here is what RSpec provides beneath RSpec-Puppet, check out RSpec Built in Matchers
To execute you the tests you created in your spec/ directory, in the root directory of the module, enter the following:
/opt/puppet/bin/rake spec