ChefSpec Tutorials
What is ChefSpec?
ChefSpec is a unit testing framework for testing Chef cookbooks. ChefSpec makes it easy to write examples and get fast feedback on cookbook changes without the need for virtual machines or cloud servers
ChefSpec runs your cookbook(s) locally with Chef Solo without actually converging a node. This has two primary benefits:
- It's really fast!
- Your tests can vary node attributes, operating systems, and search results to assert behavior under varying conditions.
Structure of Cookbooks for ChefSpec
When writing unit tests for your cookbook, the standard is to create a separate spec test for each recipe in the /spec folder.
[root@ip-172-31-21-212 webserver]# tree
.
├── Berksfile
├── chefignore
├── LICENSE
├── metadata.rb
├── README.md
├── recipes # Recipes
│ └── default.rb
│ └── recipe2.rb
├── templates # For Templates
│ └── file.erb
├── attributes # For Templates
│ └── default.rb
├── files # For Templates
│ └── index.php
dsd.txt
├── spec # UNIT TESTING
│ ├── spec_helper.rb
│ └── unit
│ └── recipes
│ └── default_spec.rb
└── recipe2_spec.rb
└── test # INTEG TESTING
└── integration
└── default
└── default_test.rb
Writing Basic ChefSpec Program
# recipe/default.rb
package 'foo'
# spec/unit/recipes/default_spec.rb
require 'chefspec'
describe 'example::default' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '16.04').converge(described_recipe) }
it 'installs foo' do
expect(chef_run).to install_package('foo')
end
end
Lets understand ChefSpec Program
require 'chefspec'
At the top of the spec file we require the chefspec gem. This is required so that our custom matchers are loaded. In larger projects, it is common practice to create a file named "spec_helper.rb" and include ChefSpec and perform other setup tasks in that file.
describe 'example::default' do
The describe keyword is part of RSpec and indicates that everything nested beneath is describing the example::default recipe. The convention is to have a separate spec for each recipe in your cookbook.
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '16.04').converge(described_recipe) }
The let block on creates the ChefSpec:SoloRunner with mocked Ohai data for Ubuntu 16.04 from Fauxhai. It then does a fake Chef run with the run_list of example::default. Any subsequent examples can then refer to chef_run in order to make assertions about the resources that were created during the mock converge.
SoloRunner
The SoloRunner allows us to simulate testing against the different operating systems (platforms) and versions by passing a parameter. It simulate the Chef run-in memory.
described_recipe
The described_recipe macro is a ChefSpec helper method that infers the recipe from the describe block. Alternatively you could specify the recipe directly.
it 'installs foo' do
The it block is an example specifying that the foo package is installed. Normally you will have multiple it blocks per recipe, each making a single assertion.
expect(chef_run).to install_package('foo')
Each of these examples defined expectations that desired packages will be installed.
expect(chef_run).to_not install_package('unzip')
Each of these examples defined expectations that desired packages not will be installed.
How to run Chef Spec Test cases?
$ chef exec spec
$ chef exec bundle exec rake
$ rspec spec/unit/recipes/default_spec.rb --color
$ chef gem install chefspec
$ rspec cookbooks//spec/default_spec.rb
$ gem install rspec
$ chef gem install rspec
$ chef exec rspec spec/unit/recipes/default_spec.rb