Test Driven
Development!
for Puppet!
Puppet needs software development
Gareth Rushgrove
Slide 2
Slide 2 text
Who
(Who is this person?)
Slide 3
Slide 3 text
@garethr
Slide 4
Slide 4 text
UK Government
Digital Service
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
No content
Slide 7
Slide 7 text
No content
Slide 8
Slide 8 text
The problem
(This isn’t a rant, but…)
Slide 9
Slide 9 text
Who here is a
software developer?
Gareth Rushgrove
Slide 10
Slide 10 text
If you’re writing
Puppet code you’re a
software developer
Gareth Rushgrove
Slide 11
Slide 11 text
As a software
developer it’s your job
to learn software
engineering practices
Gareth Rushgrove
Slide 12
Slide 12 text
What is Test Driven
Development
(And why should you care)
Slide 13
Slide 13 text
A common practice in
software engineering
Gareth Rushgrove
Slide 14
Slide 14 text
Not just testing
Gareth Rushgrove
Slide 15
Slide 15 text
Encourages simple
designs and inspires
confidence
Gareth Rushgrove
Slide 16
Slide 16 text
First write an (initially
failing) automated
test case
Gareth Rushgrove
Slide 17
Slide 17 text
Then produce the
minimum amount of
code to pass that test
Gareth Rushgrove
Slide 18
Slide 18 text
And finally refactor
the new code to
acceptable standards
Gareth Rushgrove
Slide 19
Slide 19 text
Test Driven Design
Gareth Rushgrove
Slide 20
Slide 20 text
Gareth Rushgrove
Slide 21
Slide 21 text
Unit testing
with RSpec
and Guard
(Not puppet specific)
Slide 22
Slide 22 text
A unit is the smallest
testable part of an
application
Gareth Rushgrove
Slide 23
Slide 23 text
Testing puppet
requires a little Ruby
knowledge so we’ll
use Ruby examples
Gareth Rushgrove
Slide 24
Slide 24 text
class Person
def say(word)
end
end
Gareth Rushgrove
Slide 25
Slide 25 text
First lets write a test.
For this we use the
RSpec testing
framework
Gareth Rushgrove
Slide 26
Slide 26 text
require 'person'
!
describe Person, "#say" do
it "should say something" do
!
end
end
Gareth Rushgrove
Slide 27
Slide 27 text
require 'person'
!
describe Person, "#say" do
it "should say something" do
bob = Person.new
bob.say("hello").should \
eq("hello everyone")
end
end
Gareth Rushgrove
Slide 28
Slide 28 text
Now lets run our test.
It should fail
Gareth Rushgrove
Slide 29
Slide 29 text
rspec
Gareth Rushgrove
Slide 30
Slide 30 text
Failures:
1) Person#say should say something
Failure/Error: bob.say("hello").should
eq("hello everyone")
expected: "hello everyone"
got: nil
Finished in 0.00171 seconds
1 example, 1 failure
Gareth Rushgrove
Slide 31
Slide 31 text
Now lets write the
implementation
Gareth Rushgrove
Slide 32
Slide 32 text
class Person
def say(word)
word + " everyone"
end
end
Gareth Rushgrove
Slide 33
Slide 33 text
And run our test
again
Gareth Rushgrove
Slide 34
Slide 34 text
Person#say
should say something
!
Finished in 0.00199 seconds
1 example, 0 failures
Gareth Rushgrove
Slide 35
Slide 35 text
Why not have tests
automatically run
whenever you
change the code?
Gareth Rushgrove
Slide 36
Slide 36 text
That’s what Guard
does
Gareth Rushgrove
Slide 37
Slide 37 text
guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/.+\.rb$}) { 'spec' }
end
Gareth Rushgrove
Slide 38
Slide 38 text
guard
Gareth Rushgrove
Slide 39
Slide 39 text
Lets see a quick
demo
Gareth Rushgrove
Slide 40
Slide 40 text
Why test puppet
code at all
(Testing declarative languages)
sample
should contain File[/tmp/sample]
!
Finished in 0.3881 seconds
1 example, 0 failures
Gareth Rushgrove
Slide 57
Slide 57 text
Lets run the tests
automatically
whenever you
change anything
Gareth Rushgrove
Slide 58
Slide 58 text
guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^manifests/.+\.pp$}) { 'spec' }
end
Gareth Rushgrove
Slide 59
Slide 59 text
Lets see a quick
demo of that too
Gareth Rushgrove
Slide 60
Slide 60 text
You can also test
hosts, defines, facts,
functions, hieradata
Gareth Rushgrove
Slide 61
Slide 61 text
Syntax checking,
linting, oh my
(Creating a build process)
Slide 62
Slide 62 text
puppet-lint
Gareth Rushgrove
Slide 63
Slide 63 text
Puppet!
style guide
Slide 64
Slide 64 text
Available!
as a gem
Slide 65
Slide 65 text
puppet-lint --with-filename /etc/puppet/modules
foo/manifests/bar.pp: trailing whitespace found
on line 1 apache/manifests/server.pp: variable
not enclosed in {} on line 56
Gareth Rushgrove
Test that Puppet runs
without errors
Gareth Rushgrove
Slide 95
Slide 95 text
context 'default parameters' do
it 'should work with no errors' do
pp = “class { 'sample': }”
!
expect(apply_manifest(pp).exit_code).to_not eq(1)
end
end
Gareth Rushgrove
Slide 96
Slide 96 text
Test runs are
idempotent
Gareth Rushgrove
Slide 97
Slide 97 text
context 'default parameters' do
it 'should work with no errors' do
pp = “class { 'sample': }”
!
expect(apply_manifest(pp).exit_code).to_not eq(1)
expect(apply_manifest(pp).exit_code).to eq(0)
end
end
Gareth Rushgrove
Slide 98
Slide 98 text
Test that the module
installs packages, run
services, etc.
Gareth Rushgrove
Slide 99
Slide 99 text
Gareth Rushgrove
Slide 100
Slide 100 text
describe package('nginx') do
it { should be_installed }
end
!
describe service('nginx') do
it { should be_enabled }
it { should be_running }
end
!
describe port(80) do
it { should be_listening}
end
Gareth Rushgrove