diff --git a/CHANGELOG.md b/CHANGELOG.md index cab148fb0..7af6b1e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,45 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## Unreleased -### Fixed -- Support structured facts with keys as symbols or strings - ## [2.3.0] + +Rspec-puppet now supports testing custom types, `:undef` values in params, structured facts, and checks resource dependencies recursively. + +The settings in `module_path` and `manifest` are now respected throughout the code base. The former default for `module_path` (`'/etc/puppet/modules'`) was dropped to avoid accidentally poisoning the test environment with unrelated code. + +To reduce the maintenance overhead of boilerplate code, rspec-puppet now provides some of the code that rspec-puppet-init deployed in helper files that you can just `require` instead. + +This release also reduces memory usage on bigger testsuites drastically by reducing the caching of compiled catalogs. + +### Changed +- Limit the catalogue cache to 16 entries. Significant memory savings and reduced runtime were observed in testing this. +- Prevent Puppet 3's \_timestamp fact from invalidating cache. +- Extracted catalog cache from RSpec::Puppet::Support. +- Updated README to use the rspec 3 syntax, and additional explanations. +- `contain_file(...).with_content(...)` will now only show the diff and not the full contents of the file. + ### Added -- Custom type testing example group and matcher +- Custom type testing example group and matcher. +- before/require/subscribe/notify checking now searches recursively through all dependencies. `File[a] -> File[b] -> File[c]` is now matched by `contain_file('a').that_comes_before('File[c]')`, whereas earlier versions would have missed that. +- `let(:params)` now allows `:undef` to pass a literal undef value through to the subject. +- Support structured facts with keys as symbols or strings (\#295). +- rspec-puppet-init now creates smaller files, using rspec-puppet helpers, instead of pasting code into the module. +- Added a list of related projects to the README. ### Fixed - Fix #276: `compile.and_raise_error` now correctly considers successful compilation an error - Puppet's `modulepath` can now contain multiple entries and rspec-puppet will configure puppet to load code from all of them -- `contain_file(...).with_content(...)` will now only show the diff and not the full contents of the file +- Support running with rspec 2.99 again +- non-class resources are now covered by the coverage code +- Fix #323/MODULES-2374: autorequires checking doesn't abort on "undefined method \`[]' for nil:NilClass" - improved documentation for hiera integration, added example spec - document the `scope` property -- unbreak rspec 2.99 support + +### Credits + +Thanks to Adrien Thebo, Alex Harvey, Brian, Dan Bode, Dominic Cleal, Javier Palacios, Jeff McCune, Jordan Moldow, Peter van Zetten, Raphaël Pinson, Simon Kohlmeyer, and Tristan Colgate for their contributions to this release. + + -- David Schmitt ## [2.2.0] ### Added @@ -63,7 +87,8 @@ project adheres to [Semantic Versioning](http://semver.org/). ## 1.0.1 and earlier For changelog of versions 1.0.1 and earlier, see http://rspec-puppet.com/changelog/ -[2.x]: https://github.com/rodjek/rspec-puppet/compare/v2.2.0...master +[2.x]: https://github.com/rodjek/rspec-puppet/compare/v2.3.0...master +[2.3.0]: https://github.com/rodjek/rspec-puppet/compare/v2.2.0...v2.3.0 [2.2.0]: https://github.com/rodjek/rspec-puppet/compare/v2.1.0...v2.2.0 [2.1.0]: https://github.com/rodjek/rspec-puppet/compare/v2.0.1...v2.1.0 [2.0.1]: https://github.com/rodjek/rspec-puppet/compare/v2.0.0...v2.0.1 diff --git a/README.md b/README.md index bd65b17df..8b060bd87 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ > need to pin rspec itself to `~> 3.1.0`, as later rspec versions do not work > on old rubies anymore. +## Starting out with a new module + +When you start out on a new module, run `rspec-puppet-init` to create the necessary files to configure rspec-puppet for your module's tests. + ## Naming conventions For clarity and consistency, I recommend that you use the following directory @@ -82,13 +86,13 @@ end You can test whether the subject catalog compiles cleanly with `compile`. ```ruby -it { should compile } +it { is_expected.to compile } ``` To check the error messages of your class, you can check for raised error messages. ```ruby -it { should compile.and_raise_error(/error message match/) } +it { is_expected.to compile.and_raise_error(/error message match/) } ``` #### Checking if a resource exists @@ -97,70 +101,76 @@ You can test if a resource exists in the catalogue with the generic `contain_` matcher. ```ruby -it { should contain_augeas('bleh') } +it { is_expected.to contain_augeas('bleh') } ``` You can also test if a class has been included in the catalogue with the same matcher. ```ruby -it { should contain_class('foo') } +it { is_expected.to contain_class('foo') } ``` +Note that rspec-puppet does none of the class name parsing and lookup that the puppet parser would do for you. The matcher only accepts fully qualified classnames without any leading colons. That is a class `foo::bar` will only be matched by `foo::bar`, but not by `::foo::bar`, or `bar` alone. + If your resource type includes :: (e.g. `foo::bar` simply replace the :: with __ (two underscores). ```ruby -it { should contain_foo__bar('baz') } +it { is_expected.to contain_foo__bar('baz') } ``` You can further test the parameters that have been passed to the resources with the generic `with_` chains. ```ruby -it { should contain_package('mysql-server').with_ensure('present') } +it { is_expected.to contain_package('mysql-server').with_ensure('present') } ``` If you want to specify that the given parameters should be the only ones passed to the resource, use the `only_with_` chains. ```ruby -it { should contain_package('httpd').only_with_ensure('latest') } +it { is_expected.to contain_package('httpd').only_with_ensure('latest') } ``` You can use the `with` method to verify the value of multiple parameters. ```ruby -it do should contain_service('keystone').with( - 'ensure' => 'running', - 'enable' => 'true', - 'hasstatus' => 'true', - 'hasrestart' => 'true' -) end +it do + is_expected.to contain_service('keystone').with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true', + 'hasrestart' => 'true' + ) +end ``` The same holds for the `only_with` method, which in addition verifies the exact set of parameters and values for the resource in the catalogue. ```ruby -it do should contain_user('luke').only_with( - 'ensure' => 'present', - 'uid' => '501' -) end +it do + is_expected.to contain_user('luke').only_with( + 'ensure' => 'present', + 'uid' => '501' + ) +end ``` You can also test that specific parameters have been left undefined with the generic `without_` chains. ```ruby -it { should contain_file('/foo/bar').without_mode } +it { is_expected.to contain_file('/foo/bar').without_mode } ``` You can use the without method to verify that a list of parameters have not been defined ```ruby -it { should contain_service('keystone').without( +it { is_expected.to contain_service('keystone').without( ['restart', 'status'] )} ``` @@ -171,28 +181,28 @@ You can test the number of resources in the catalogue with the `have_resource_count` matcher. ```ruby -it { should have_resource_count(2) } +it { is_expected.to have_resource_count(2) } ``` The number of classes in the catalogue can be checked with the `have_class_count` matcher. ```ruby -it { should have_class_count(2) } +it { is_expected.to have_class_count(2) } ``` You can also test the number of a specific resource type, by using the generic `have__resource_count` matcher. ```ruby -it { should have_exec_resource_count(1) } +it { is_expected.to have_exec_resource_count(1) } ``` This last matcher also works for defined types. If the resource type contains ::, you can replace it with __ (two underscores). ```ruby -it { should have_logrotate__rule_resource_count(3) } +it { is_expected.to have_logrotate__rule_resource_count(3) } ``` *NOTE*: when testing a class, the catalogue generated will always contain at @@ -205,19 +215,19 @@ catalogue generated when testing a defined type will have at least one resource The following methods will allow you to test the relationships between the resources in your catalogue, regardless of how the relationship is defined. This means that it doesn’t matter if you prefer to define your relationships with the metaparameters (**require**, **before**, **notify** and **subscribe**) or the chaining arrows (**->**, **~>**, **<-** and **<~**), they’re all tested the same. ```ruby -it { should contain_file('foo').that_requires('File[bar]') } -it { should contain_file('foo').that_comes_before('File[bar]') } -it { should contain_file('foo').that_notifies('File[bar]') } -it { should contain_file('foo').that_subscribes_to('File[bar]') } +it { is_expected.to contain_file('foo').that_requires('File[bar]') } +it { is_expected.to contain_file('foo').that_comes_before('File[bar]') } +it { is_expected.to contain_file('foo').that_notifies('File[bar]') } +it { is_expected.to contain_file('foo').that_subscribes_to('File[bar]') } ``` An array can be used to test a resource for multiple relationships ```ruby -it { should contain_file('foo').that_requires(['File[bar]', 'File[baz]']) } -it { should contain_file('foo').that_comes_before(['File[bar]','File[baz]']) } -it { should contain_file('foo').that_notifies(['File[bar]', 'File[baz]']) } -it { should contain_file('foo').that_subscribes_to(['File[bar]', 'File[baz]']) } +it { is_expected.to contain_file('foo').that_requires(['File[bar]', 'File[baz]']) } +it { is_expected.to contain_file('foo').that_comes_before(['File[bar]','File[baz]']) } +it { is_expected.to contain_file('foo').that_notifies(['File[bar]', 'File[baz]']) } +it { is_expected.to contain_file('foo').that_subscribes_to(['File[bar]', 'File[baz]']) } ``` You can also test the reverse direction of the relationship, so if you have the following bit of Puppet code @@ -232,13 +242,14 @@ notify { 'bar': You can test that **Notify[bar]** comes before **Notify[foo]** ```ruby -it { should contain_notify('bar').that_comes_before('Notify[foo]') } +it { is_expected.to contain_notify('bar').that_comes_before('Notify[foo]') } ``` Or, you can test that **Notify[foo]** requires **Notify[bar]** ```ruby -it { should contain_notify('foo').that_requires('Notify[bar]') } +it { is_expected.to contain_notify('foo').that_requires('Notify[bar]') } ``` + ##### Match target syntax Note that this notation does not support any of the features you're used from the puppet language. Only a single resource with a single, unquoted title can be referenced here. Class names need to be always fully qualified and not have the leading `::`. It currently does not support inline arrays or quoting. @@ -252,17 +263,7 @@ These will not work * `Notify[foo, bar]` * `Class[::profile::apache]` -#### Type matcher - -When testing custom types, the `be_valid_type` matcher provides a range of expectations: - -* `with_provider()`: check that the right provider was selected -* `with_properties()`: check that the specified properties are available -* `with_parameters()`: check that the specified parameters are available -* `with_features()`: check that the specified features are available -* `with_set_attributes()`: check that the specified attributes are set - -#### Recursive dependencies +##### Recursive dependencies The relationship matchers are recursive in two directions: @@ -283,7 +284,6 @@ class foo::config { } ``` - * horizontal recursion, which follows indirect dependencies (dependencies of dependencies). E.g. where `Yumrepo['foo']` comes before `File['/foo']`: @@ -305,10 +305,19 @@ class foo::config { } ``` -#### Autorequires +##### Autorequires Autorequires are considered in dependency checks. +#### Type matcher + +When testing custom types, the `be_valid_type` matcher provides a range of expectations: + +* `with_provider()`: check that the right provider was selected +* `with_properties()`: check that the specified properties are available +* `with_parameters()`: check that the specified parameters are available +* `with_features()`: check that the specified features are available +* `with_set_attributes()`: check that the specified attributes are set ### Writing tests @@ -333,7 +342,7 @@ describe 'sysctl' do let(:title) { 'baz' } let(:params) { { :value => 'foo' } } - it { should contain_exec('sysctl/reload').with_command("/sbin/sysctl -p /etc/sysctl.conf") } + it { is_expected.to contain_exec('sysctl/reload').with_command("/sbin/sysctl -p /etc/sysctl.conf") } end ``` @@ -343,7 +352,7 @@ end let(:title) { 'foo' } ``` -#### Specifying the parameters to pass to a resources or parametised class +#### Specifying the parameters to pass to a resources or parameterised class ```ruby let(:params) { {:ensure => 'present', ...} } @@ -437,7 +446,7 @@ For your convenience though, a `run` matcher exists to provide easier to understand test cases. ```ruby -it { should run.with_params('foo').and_return('bar') } +it { is_expected.to run.with_params('foo').and_return('bar') } ``` ### Writing tests @@ -466,7 +475,7 @@ You can specify the arguments to pass to your function during the test(s) using either the `with_params` chain method in the `run` matcher ```ruby -it { should run.with_params('foo', 'bar', ['baz']) } +it { is_expected.to run.with_params('foo', 'bar', ['baz']) } ``` Or by using the `call` method on the subject directly @@ -483,7 +492,7 @@ You can test the result of a function (if it produces one) using either the `and_returns` chain method in the `run` matcher ```ruby -it { should run.with_params('foo').and_return('bar') } +it { is_expected.to run.with_params('foo').and_return('bar') } ``` Or by using any of the existing RSpec matchers on the subject directly @@ -501,8 +510,8 @@ You can test whether the function throws an exception using either the `and_raises_error` chain method in the `run` matcher ```ruby -it { should run.with_params('a', 'b').and_raise_error(Puppet::ParseError) } -it { should_not run.with_params('a').and_raise_error(Puppet::ParseError) } +it { is_expected.to run.with_params('a', 'b').and_raise_error(Puppet::ParseError) } +it { is_expected.not_to run.with_params('a').and_raise_error(Puppet::ParseError) } ``` Or by using the existing `raises_error` RSpec matcher @@ -521,7 +530,7 @@ stubbing other parts of the system. ```ruby before(:each) { scope.expects(:lookupvar).with('some_variable').returns('some_value') } -it { should run.with_params('...').and_return('...') } +it { is_expected.to run.with_params('...').and_return('...') } ``` ## Hiera integration @@ -612,3 +621,5 @@ list of untouched resources. * [rspec-puppet-facts](https://github.com/mcanevet/rspec-puppet-facts): Simplify your unit tests by looping on every supported Operating System and populating facts. * [rspec-puppet-osmash](https://github.com/Aethylred/rspec-puppet-osmash): Provides Operation System hashes and validations for rspec-puppet * [puppet_spec_facts](https://github.com/danieldreier/puppet_spec_facts): Gem to provide puppet fact hashes for rspec-puppet testing + +For a list of other module development tools see https://puppet.community/plugins/ diff --git a/rspec-puppet.gemspec b/rspec-puppet.gemspec index 9e84ebcb6..f7f283d45 100644 --- a/rspec-puppet.gemspec +++ b/rspec-puppet.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'rspec-puppet' - s.version = '2.2.1.pre' + s.version = '2.3.0' s.homepage = 'https://github.com/rodjek/rspec-puppet/' s.summary = 'RSpec tests for your Puppet manifests' s.description = 'RSpec tests for your Puppet manifests'