rspec-puppet-utils 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +7 -0
- data/README.md +97 -0
- data/Rakefile +6 -0
- data/lib/rspec-puppet-utils.rb +64 -0
- data/rspec-puppet-utils.gemspec +14 -0
- data/spec/classes/mock_function_spec.rb +85 -0
- data/spec/classes/template_harness_spec.rb +46 -0
- data/spec/fixtures/templates/returns_elephant.erb +1 -0
- data/spec/spec_helper.rb +6 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA512:
|
3
|
+
metadata.gz: 73d31647aca9a8ea92a51e91875d9428ddf36d65522e540205bd22fc056c577c025bac30e777479234c390977a1b13d815a64ebb5a33e742543e489b59493d80
|
4
|
+
data.tar.gz: 875c88ed3de884c3acdf8341e3dc675d011fba4f04b112ac3f9cb370580af86ba5ec9d256c0f82c62787b151035903bc1e5e40b11b3f8b1a1ac8cfab6026c2a6
|
5
|
+
SHA1:
|
6
|
+
metadata.gz: a44abeac9ab5e7f480bc8a9e0d1587cd41e8d596
|
7
|
+
data.tar.gz: 69010a95237c2e8c0c0e8777b266e98d7c00e3ae
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# rspec-puppet-utils
|
2
|
+
|
3
|
+
This is the continuation of a previous project about [rspec-puppet unit testing](https://github.com/TomPoulton/rspec-puppet-unit-testing), it provides a more refined version of the helper method for mocking functions, plus a harness for testing templates. The motivation for mocking functions etc is provided there so I won't go over it here.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
### MockFunction
|
8
|
+
|
9
|
+
The basic usage is to create your mock function with `MockFunction.new` and then use `mocha` to stub any particular calls that you need
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'spec_helper'
|
13
|
+
|
14
|
+
describe 'foo::bar' do
|
15
|
+
|
16
|
+
add_stuff = MockFunction.new(self, 'add_stuff')
|
17
|
+
before(:each) do
|
18
|
+
add_stuff.stubs(:call).with([1, 2]).returns(3)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should do something with add_stuff' do
|
22
|
+
...
|
23
|
+
end
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
You can specify a default value:
|
28
|
+
```ruby
|
29
|
+
func = MockFunction.new(self, 'func', {:default_value => true})
|
30
|
+
```
|
31
|
+
|
32
|
+
You can mock a function that doesn't return a value (`:rvalue` is the default):
|
33
|
+
```ruby
|
34
|
+
func = MockFunction.new(self, 'func', {:type => :statement})
|
35
|
+
```
|
36
|
+
|
37
|
+
You can mock Hiera:
|
38
|
+
```ruby
|
39
|
+
hiera = MockFunction.new(self, 'hiera')
|
40
|
+
before(:each) do
|
41
|
+
hiera.stubs(:call).with(['non-ex']).raises(Puppet::ParseError.new('Key not found'))
|
42
|
+
hiera.stubs(:call).with(['db-password']).returns('password1')
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Note:
|
47
|
+
- You always stub the `call` method as that gets called internally
|
48
|
+
- The `call` method takes an array of arguments
|
49
|
+
- `self` is a way of getting hold of the current `RSpec::Core::ExampleGroup` instance. If anyone knows how to do this more cleanly let me know!
|
50
|
+
|
51
|
+
### TemplateHarness
|
52
|
+
|
53
|
+
If your templates have some logic in them that you want to test, and you'd ideally like to get hold of the generated template so you can inspect it programatically rather than just using a regex then use `TemplateHarness`
|
54
|
+
|
55
|
+
Given a basic template:
|
56
|
+
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
<%
|
60
|
+
from_class = @class_var
|
61
|
+
from_fact = scope.lookupvar('fact-name')
|
62
|
+
from_hiera = scope.function_hiera('hiera-key')
|
63
|
+
-%>
|
64
|
+
<%= "#{from_class} #{from_fact} #{from_hiera}" %>
|
65
|
+
|
66
|
+
```
|
67
|
+
|
68
|
+
A test could look like this:
|
69
|
+
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
require 'spec_helper'
|
73
|
+
|
74
|
+
describe 'my_template' do
|
75
|
+
|
76
|
+
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
|
77
|
+
before(:each) do
|
78
|
+
scope.stubs(:lookupvar).with('fact-name').returns('fact-value')
|
79
|
+
scope.stubs(:function_hiera).with('hiera-key').returns('hiera-value')
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should render template' do
|
83
|
+
harness = TemplateHarness.new('spec/.../.../my_template.erb', scope)
|
84
|
+
harness.set('@class_var', 'classy')
|
85
|
+
result = harness.run
|
86
|
+
expect(result).to eq 'classy fact-value hiera-value'
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
Note:
|
93
|
+
- The path resolution is pretty simple, just pass it a normal relative path, **not** like the paths you pass into the `template` function in puppet (where you expect puppet to add the `templates` section to the path)
|
94
|
+
|
95
|
+
## Setup
|
96
|
+
- Add `rspec-puppet-utils` to your Gemfile (or use `gem install rspec-puppet-utils`)
|
97
|
+
- Add `require 'rspec-puppet-utils'` to the top of your `spec_helper`
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rspec-puppet'
|
2
|
+
require 'mocha'
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
RSpec.configure do |c|
|
6
|
+
c.mock_with :mocha
|
7
|
+
end
|
8
|
+
|
9
|
+
class MockFunction
|
10
|
+
|
11
|
+
attr_accessor :function_type, :has_default_value, :default_value
|
12
|
+
|
13
|
+
def initialize(example_group, name, options = {})
|
14
|
+
opts = options.nil? ? {} : options
|
15
|
+
|
16
|
+
@function_type = opts.has_key?(:type) ? opts[:type] : :rvalue
|
17
|
+
|
18
|
+
opts[:default_value] = nil if @function_type == :statement
|
19
|
+
|
20
|
+
@has_default_value = false
|
21
|
+
if opts.has_key?(:default_value)
|
22
|
+
@has_default_value = true
|
23
|
+
@default_value = opts[:default_value]
|
24
|
+
end
|
25
|
+
|
26
|
+
this = self
|
27
|
+
example_group.before(:each) {
|
28
|
+
Puppet::Parser::Functions.newfunction(name.to_sym, {:type => this.function_type}) { |args| this.call(args) }
|
29
|
+
this.stubs(:call).returns(this.default_value) if this.has_default_value
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TemplateHarness
|
35
|
+
|
36
|
+
def initialize(template, scope = nil)
|
37
|
+
@template = template
|
38
|
+
@isolator = Isolator.new(scope)
|
39
|
+
end
|
40
|
+
|
41
|
+
def set(name, value)
|
42
|
+
var_name = name.start_with?('@') ? name : "@#{name}"
|
43
|
+
@isolator.instance_variable_set(var_name, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
b = @isolator.get_binding
|
48
|
+
template = File.exists?(@template) ? File.new(@template).read : @template
|
49
|
+
ERB.new(template, 0, '-').result b
|
50
|
+
end
|
51
|
+
|
52
|
+
class Isolator
|
53
|
+
# Isolates the binding so that only the defined set
|
54
|
+
# of instance variables are available to erb
|
55
|
+
def initialize scope
|
56
|
+
@scope = scope
|
57
|
+
end
|
58
|
+
def get_binding
|
59
|
+
scope = @scope
|
60
|
+
binding
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |gem|
|
3
|
+
gem.name = 'rspec-puppet-utils'
|
4
|
+
gem.version = '1.0.0'
|
5
|
+
gem.description = 'Helper classes for mock functions and templates'
|
6
|
+
gem.summary = ''
|
7
|
+
gem.author = 'Tom Poulton'
|
8
|
+
#gem.license = ''
|
9
|
+
|
10
|
+
gem.homepage = 'https://github.com/Accuity/rspec-puppet-utils'
|
11
|
+
gem.files = `git ls-files`.split($/)
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
+
gem.require_paths = ['lib']
|
14
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec-puppet-utils'
|
3
|
+
|
4
|
+
describe MockFunction do
|
5
|
+
|
6
|
+
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
|
7
|
+
|
8
|
+
context 'with no options' do
|
9
|
+
|
10
|
+
func = MockFunction.new(self, 'func')
|
11
|
+
|
12
|
+
it 'should return an object' do
|
13
|
+
expect(func).to_not eq nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should not attach stub method to object' do
|
17
|
+
expect { func.call 'a' }.to raise_error NoMethodError
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should set function_type to :rvalue' do
|
21
|
+
expect(func.function_type).to eq :rvalue
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with options set to nil' do
|
27
|
+
|
28
|
+
it 'should not throw error creating function' do
|
29
|
+
expect { MockFunction.new(example.example_group, 'nil_options', nil) }.to_not raise_error
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with a default value of true' do
|
35
|
+
|
36
|
+
default_of_true = MockFunction.new(self, 'default_of_true', {:default_value => true})
|
37
|
+
|
38
|
+
it 'function should return true' do
|
39
|
+
expect(default_of_true.call 'a').to eq true
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with a default value of nil' do
|
45
|
+
|
46
|
+
default_of_nil = MockFunction.new(self, 'default_of_nil', {:default_value => nil})
|
47
|
+
|
48
|
+
it 'function should return true' do
|
49
|
+
expect(default_of_nil.call 'a').to eq nil
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a type of :statement' do
|
55
|
+
|
56
|
+
statement = MockFunction.new(self, 'statement', {:type => :statement})
|
57
|
+
|
58
|
+
it 'should set function_type to :statement' do
|
59
|
+
expect(statement.function_type).to eq :statement
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should wire up a stub call method' do
|
63
|
+
expect { statement.call }.to_not raise_error
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when using a puppet scope' do
|
69
|
+
|
70
|
+
func = MockFunction.new(self, 'func', {:default_value => true})
|
71
|
+
|
72
|
+
it 'puppet should be able to call function' do
|
73
|
+
result = scope.function_func ['a']
|
74
|
+
expect(result).to eq true
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should be able to stub calls' do
|
78
|
+
func.stubs(:call).with([1, 2]).returns(3)
|
79
|
+
result = scope.function_func [1, 2]
|
80
|
+
expect(result).to eq 3
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec-puppet-utils'
|
3
|
+
|
4
|
+
describe TemplateHarness do
|
5
|
+
|
6
|
+
it 'should render template' do
|
7
|
+
harness = TemplateHarness.new('<%= "inside template" %>')
|
8
|
+
expect(harness.run).to eq 'inside template'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should handle -%> syntax' do
|
12
|
+
harness = TemplateHarness.new('<% animal = "penguin" -%><%= animal %>')
|
13
|
+
expect { harness.run }.to_not raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should provide access to scope' do
|
17
|
+
scope = PuppetlabsSpec::PuppetInternals.scope
|
18
|
+
scope.stubs(:lookupvar).with('honey').returns('badger')
|
19
|
+
harness = TemplateHarness.new('<%= scope.lookupvar("honey") %>', scope)
|
20
|
+
expect(harness.run).to eq 'badger'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should provide access to instance vars' do
|
24
|
+
harness = TemplateHarness.new('<%= @foo %>')
|
25
|
+
harness.set('@foo', 'bar')
|
26
|
+
expect(harness.run).to eq 'bar'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should add @ to instance vars when missing' do
|
30
|
+
harness = TemplateHarness.new('<%= @alice %>')
|
31
|
+
harness.set('alice', 'bob')
|
32
|
+
expect(harness.run).to eq 'bob'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should isolate instance vars' do
|
36
|
+
harness = TemplateHarness.new('<%= @not_exist %>')
|
37
|
+
harness.instance_variable_set('@not_exist', 'pixies')
|
38
|
+
expect(harness.run).to eq ''
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should read file if it exists' do
|
42
|
+
harness = TemplateHarness.new('spec/fixtures/templates/returns_elephant.erb')
|
43
|
+
expect(harness.run).to eq 'elephant'
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= 'elephant' %>
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-puppet-utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Poulton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2014-03-04 00:00:00 Z
|
13
|
+
dependencies: []
|
14
|
+
|
15
|
+
description: Helper classes for mock functions and templates
|
16
|
+
email:
|
17
|
+
executables: []
|
18
|
+
|
19
|
+
extensions: []
|
20
|
+
|
21
|
+
extra_rdoc_files: []
|
22
|
+
|
23
|
+
files:
|
24
|
+
- .gitignore
|
25
|
+
- Gemfile
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- lib/rspec-puppet-utils.rb
|
29
|
+
- rspec-puppet-utils.gemspec
|
30
|
+
- spec/classes/mock_function_spec.rb
|
31
|
+
- spec/classes/template_harness_spec.rb
|
32
|
+
- spec/fixtures/templates/returns_elephant.erb
|
33
|
+
- spec/spec_helper.rb
|
34
|
+
homepage: https://github.com/Accuity/rspec-puppet-utils
|
35
|
+
licenses: []
|
36
|
+
|
37
|
+
metadata: {}
|
38
|
+
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- &id001
|
47
|
+
- ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- *id001
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.0.14
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: ""
|
60
|
+
test_files:
|
61
|
+
- spec/classes/mock_function_spec.rb
|
62
|
+
- spec/classes/template_harness_spec.rb
|
63
|
+
- spec/fixtures/templates/returns_elephant.erb
|
64
|
+
- spec/spec_helper.rb
|