1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
### Test that the catalogue compiles
This is the most basic test that can be done on a manifest. It will test that
the manifest can be compiled into a catalogue, and that the catalogue has no
dependency cycles between resources.
{% highlight ruby %}
it { is_expected.to compile }
{% endhighlight %}
This matcher has an optional method that can be chained onto it in order to
have rspec-puppet test that all relationships in the catalogue (as defined with
`require`, `notify`, `subscribe`, `before`, or the chaining arrows) resolve to
resources in the catalogue.
{% highlight ruby %}
it { is_expected.to compile.with_all_deps }
{% endhighlight %}
### Test for errors
When testing for an expected error (e.g. testing the behaviour of input
validation), the `and_raise_error` method should be chained onto the `compile`
matcher.
{% highlight ruby %}
describe 'my::type' do
context 'with ensure => present' do
let(:params) { {'ensure' => 'present'} }
it { is_expected.to compile }
end
context 'with ensure => whoopsiedoo' do
let(:params) { {'ensure' => 'whoopsiedoo'} }
it { is_expected.to compile.and_raise_error(/the expected error message/) }
end
end
{% endhighlight %}
### Test a resource
The presence of a resource in the catalogue can be tested using the generic
`contain_<resource type>` matcher.
{% highlight ruby %}
it { is_expected.to contain_service('apache2') }
{% endhighlight %}
If the `<resource type>` includes `::` (e.g. the `apache::vhost` defined type),
you must replace the `::` with `__` (two underscores) in the matcher name.
{% highlight ruby %}
it { is_expected.to contain_apache__vhost('www.mysite.com') }
{% endhighlight %}
This can also be used to test if a class has been included in the catalogue.
{% highlight ruby %}
it { is_expected.to contain_class('apache::vhosts') }
{% endhighlight %}
<div class="callout-block callout-info">
<div class="icon-holder"><i class="fa fa-info-circle"></i></div>
<div class="content">
rspec-puppet does not do the class name parsing and lookup that the Puppet
parser would do for you. The matcher only accepts fully qualified class names
without any leading colons. This means that class <code>foo::bar</code> will only be
matched by <code>foo::bar</code>, not by <code>::foo::bar</code> or <code>bar</code> alone.
</div>
</div>
### Test resource parameters
The values of a resource's parameters can be tested by chaining
`with_<parameter name>(<value>)` methods onto the `contain_<resource type>`
matcher.
{% highlight ruby %}
it { is_expected.to contain_apache__vhost('www.mysite.com').with_ensure('present') }
{% endhighlight %}
While you can chain multiple `with_<parameter name>` methods together, it may
be cleaner for a large number of parameters to instead to chain the `with` method
and pass a hash of expected parameters and values instead.
{% highlight ruby %}
it { is_expected.to contain_service('apache').with('ensure' => 'present', 'enable' => true) }
# is equivalent to
it { is_expected.to contain_service('apache').with_ensure('present').with_enable(true) }
{% endhighlight %}
Testing parameters using `with_<parameter name>` or `with` will not take into
account any other parameters that might be set on the resource. In order to
test that **only** the specificied parameters have been set on a resource, the
`only_with_<parameter name>` method can be chained onto the
`contain_<resource type>` matcher.
{% highlight ruby %}
# If any parameters have been set on Package[httpd] other than ensure, this test will fail.
it { is_expected.to contain_package('httpd').only_with_ensure('latest') }
{% endhighlight %}
Similarly to `with_<parameter name>`, there exists a way to specify multiple
parameters at once, by chaining `only_with` onto the `contain_<resource type>`
matcher and passing it a hash of expected parameters and values.
{% highlight ruby %}
it { is_expected.to contain_service('apache').only_with('ensure' => 'running', 'enable' => true) }
{% endhighlight %}
Lastly, there are situations where it is necessary to test that certain
parameters **have not** been set on a resource. This can be done by chaining
`without_<parameter name>` methods onto the `contain_<resource type>` matcher.
{% highlight ruby %}
it { is_expected.to contain_file('/tmp/testfile').without_mode }
{% endhighlight %}
As with the other parameter methods, there is a way to specify multiple
undefined parameters at once by chaining the `without` method to the
`contain_<resource type>` matcher and passing it an array of parameter names.
{% highlight ruby %}
it { is_expected.to contain_service('apache').without(['restart', 'status']) }
{% endhighlight %}
It can be tested that a Sensitive value of resource parameter passed to template is present as expected.
{% highlight ruby %}
it { is_expected.to contain_file('/etc/mysecret.conf').with_content(sensitive(%r{^Token==MySecretTokenValue$})) }
{% endhighlight %}
### Test resource parameter values for uniqueness
Use the `have_unique_values_for_all` matcher to test a specific resource parameter
for uniqueness of values across the entire catalogue:
{% highlight ruby %}
it { is_expected.to have_unique_values_for_all('user', 'uid')
{% endhighlight %}
### Testing relationships between resources
The relationships between resources can be tested using the following methods,
regardless of how the relationship has been defined. This mean that it doesn't
matter if it was defined using the relationship metaparameters (`require`,
`before`, `notify`, `subscribe`) or the chaining arrows (`->`, `<-`, `~>`,
`<~`).
<div class="callout-block callout-info">
<div class="icon-holder"><i class="fa fa-info-circle"></i></div>
<div class="content">
The values passed to these methods must be in the format used in the Puppet
catalogue (which is slightly different to the way they're written in a Puppet
manifest).
<ul>
<li>The resource titles must be unquoted (<code>Package[apache]</code> instead of <code>Package['apache']</code>)</li>
<li>One title per resource (<code>[Package[apache], Package[htpasswd]]</code> instead of <code>Package[apache, htpasswd]</code>)</li>
<li>If referencing a class, it must be fully qualified and should not have a leading <code>::</code> (<code>Class[apache::service]</code> instead of <code>Class[::apache::service]</code>)</li>
</ul>
</div>
</div>
{% highlight ruby %}
it { is_expected.to contain_file('a').that_requires('File[b]') }
it { is_expected.to contain_file('a').that_comes_before('File[b]') }
it { is_expected.to contain_file('a').that_notifies('File[b]') }
it { is_expected.to contain_file('a').that_subscribes_to('File[b]') }
{% endhighlight %}
An array can be passed if the resource has the same type of relationship to
multiple resources.
{% highlight ruby %}
it { is_expected.to contain_file('a').that_requires(['File[b]', 'File[c]']) }
it { is_expected.to contain_file('a').that_comes_before(['File[b]', 'File[c]']) }
it { is_expected.to contain_file('a').that_notifies(['File[b]', 'File[c]']) }
it { is_expected.to contain_file('a').that_subscribes_to(['File[b]', 'File[c]']) }
{% endhighlight %}
The relationships can be tested in either direction, so given the following
manifest:
{% highlight puppet %}
notify { 'a': }
notify { 'b':
before => Notify['a'],
}
{% endhighlight %}
It can be tested that `Notify[b]` comes before `Notify[a]`
{% highlight ruby %}
it { is_expected.to contain_notify('b').that_comes_before('Notify[a]') }
{% endhighlight %}
Or that `Notify[a]` requires `Notify[b]`
{% highlight ruby %}
it { is_expected.to contain_notify('a').that_requires('Notify[b]') }
{% endhighlight %}
### Testing the total number of resources
The total number of resources in the catalogue can be tested with the
`have_resource_count` matcher.
{% highlight ruby %}
it { is_expected.to have_resource_count(2) }
{% endhighlight %}
### Testing the total number of classes
The total number of classes in the catalogue can be tested with the
`have_class_count` matcher.
{% highlight ruby %}
it { is_expected.to have_class_count(4) }
{% endhighlight %}
### Testing the number of resources of a specific type
The number of resources of a specific type can be tested using the generic
`have_<resource type>_resource_count` matcher.
{% highlight ruby %}
it { is_expected.to have_exec_resource_count(1) }
{% endhighlight %}
As with the generic `contain_<resource type>` matcher, this matcher can also be
used for defined types that contain `::` in their name by replacing the `::`
with `__` (two underscores).
{% highlight ruby %}
it { is_expected.to have_apache__vhost_resource_count(3) }
{% endhighlight %}
|