Sharing Serverspec examples
Following on from last weeks post, this post is going to go over sharing Serverspec examples between multiple hosts.
Default setup
After running serverspec-init
for a few hosts you will end up with a folder
structure that looks something like this:
.
+-- Rakefile
+-- spec
+-- remote-a.example.com
¦ +-- sample_spec.rb
+-- remote-b.example.com
¦ +-- sample_spec.rb
+-- remote-c.example.com
¦ +-- sample_spec.rb
+-- spec_helper.rb
When you run rake spec
the following happens:
-
rake
parses theRakefile
and runs the:spec
task. -
The
:spec
task finds all of the directories under thespec
directory and creates a task for each host:RSpec::Core::RakeTask.new(target.to_sym) do |t| ENV['TARGET_HOST'] = original_target t.pattern = "spec/#{original_target}/*_spec.rb" end
-
Each host task then invokes
rspec
to run the spec files for each host. -
Finally spec files will load
spec_helper.rb
and declare the tests which rspec should run.
This is fine if you only have a handful of hosts, however if you have hundreds of hosts, duplicate test code will quickly become a problem.
Creating shared examples
Shared examples will look something like this:
shared_examples 'webserver' do
describe package('httpd') do
it { should be_installed }
end
describe service('httpd') do
it { should be_enabled }
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
end
For this post, shared examples are going to be kept in a directory called
spec/shared/
. So the code above would be saved in spec/shared/webserver.rb
;
however feel free to use an alternative directory.
Note: you can include multiple shared examples in a single file, however sticking to one file per shared example makes the examples easier to find and manage.
Because the shared
directory is in the spec
directory it will be picked up
by the :spec
task. This can be fixed by making the following change to the
rakefile.
namespace :spec do
targets = []
Dir.glob('./spec/*').each do |dir|
next unless File.directory?(dir)
+ next if File.basename(dir) == 'shared'
target = File.basename(dir)
target = "_#{target}" if target == "default"
targets << target
end
Before the shared examples can be used, they need to be imported by each spec
file. This can be done easily by adding the following lines to the
spec_helper.rb
file:
# Load shared examples
base_spec_dir = Pathname.new(File.join(File.dirname(__FILE__)))
Dir[base_spec_dir.join('shared/**/*.rb')].sort.each{ |f| require f }
Finally once shared examples are loaded, they can be included in host spec files:
$ cat spec/remote-a.example.com/main_spec.rb
require 'spec_helper'
describe 'remote-a.example.com' do
include_examples 'webserver'
end
$ cat spec/remote-b.example.com/main_spec.rb
require 'spec_helper'
describe 'remote-b.example.com' do
include_examples 'webserver'
end