{"id":13777865,"url":"https://github.com/rodjek/rspec-puppet","last_synced_at":"2025-04-08T09:06:10.572Z","repository":{"id":47691303,"uuid":"2063660","full_name":"rodjek/rspec-puppet","owner":"rodjek","description":"RSpec tests for your Puppet manifests","archived":false,"fork":false,"pushed_at":"2023-10-12T18:13:31.000Z","size":2253,"stargazers_count":362,"open_issues_count":69,"forks_count":202,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-03-30T07:32:43.278Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://rspec-puppet.com","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rodjek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2011-07-17T23:32:11.000Z","updated_at":"2024-12-25T23:10:31.000Z","dependencies_parsed_at":"2024-01-16T12:49:33.334Z","dependency_job_id":"30ba8639-b390-48ad-bc34-570859c26c6d","html_url":"https://github.com/rodjek/rspec-puppet","commit_stats":null,"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodjek%2Frspec-puppet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodjek%2Frspec-puppet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodjek%2Frspec-puppet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodjek%2Frspec-puppet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rodjek","download_url":"https://codeload.github.com/rodjek/rspec-puppet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809963,"owners_count":20999816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T18:00:49.394Z","updated_at":"2025-04-08T09:06:10.553Z","avatar_url":"https://github.com/rodjek.png","language":"Ruby","funding_links":[],"categories":["Testing"],"sub_categories":[],"readme":"# RSpec tests for your Puppet manifests \u0026 modules\n[![Build Status](https://travis-ci.org/rodjek/rspec-puppet.svg?branch=master)](https://travis-ci.org/rodjek/rspec-puppet)\n[![Coverage Status](https://coveralls.io/repos/rodjek/rspec-puppet/badge.svg?branch=master)](https://coveralls.io/r/rodjek/rspec-puppet?branch=master)\n\n\n--------\n\nNote that as of release 2.10.0, this project is being maintained in the main [`puppetlabs namespace`](https://github.com/puppetlabs/rspec-puppet).\n\n--------\n\n#### Table of Contents\n\n* [Installation](#installation)\n* [Starting out with a new module](#starting-out-with-a-new-module)\n* [Configure manifests for Puppet 4](#configure-manifests-for-puppet-4)\n* [Configuration](#configuration)\n* [Naming conventions](#naming-conventions)\n* [Example groups](#example-groups)\n* [Defined Types, Classes \u0026 Applications](#defined-types-classes--applications)\n* [Functions](#functions)\n* [Hiera integration](#hiera-integration)\n* [Producing coverage reports](#producing-coverage-reports)\n* [Related projects](#related-projects)\n\n## Installation\n\n    gem install rspec-puppet\n\n\u003e Note for ruby 1.8 users:  while rspec-puppet itself supports ruby 1.8, you'll\n\u003e need to pin rspec itself to `~\u003e 3.1.0`, as later rspec versions do not work\n\u003e on old rubies anymore.\n\n## Starting out with a new module\n\nWhen you start out on a new module, create a metadata.json file for your module and then run `rspec-puppet-init` to create the necessary files to configure rspec-puppet for your module's tests.\n\n\n## Configure manifests for Puppet 4\n\nWith Puppet 3, the manifest is set to `$manifestdir/site.pp`. However Puppet 4 defaults to an empty value. In order to test manifests you will need to set appropriate settings.\n\nPuppet configuration reference for `manifest` can be found online:\n\n* Puppet 3: https://docs.puppet.com/puppet/3.8/reference/configuration.html#manifest\n* Puppet 4: https://docs.puppet.com/puppet/4.8/reference/configuration.html#manifest\n\nConfiguration is typically done in a `spec/spec_helper.rb` file which each of your spec will require. Example code:\n```ruby\n# /spec\nbase_dir = File.dirname(File.expand_path(__FILE__))\n\nRSpec.configure do |c|\n  c.module_path     = File.join(base_dir, 'fixtures', 'modules')\n  c.manifest_dir    = File.join(base_dir, 'fixtures', 'manifests')\n  c.manifest        = File.join(base_dir, 'fixtures', 'manifests', 'site.pp')\n  c.environmentpath = File.join(Dir.pwd, 'spec')\n\n  # Coverage generation\n  c.after(:suite) do\n    RSpec::Puppet::Coverage.report!\n  end\nend\n```\n\n## Configuration\n\nrspec-puppet can be configured by modifying the `RSpec.configure` block in your\n`spec/spec_helper.rb` file.\n\n```\nRSpec.configure do |c|\n  c.\u003cconfig option\u003e = \u003cvalue\u003e\nend\n```\n\n#### manifest\\_dir\nType   | Default  | Puppet Version(s)\n------ | -------- | -----------------\nString | Required | 2.x, 3.x\n\nThe path to the directory containing your basic manifests like `site.pp`.\n\n#### module\\_path\nType   | Default  | Puppet Version(s)\n------ | -------- | ------------------\nString | Required | 2.x, 3.x, 4.x, 5.x\n\nThe path to the directory containing your Puppet modules.\n\n#### default\\_facts\nType | Default | Puppet Version(s)\n---- | ------- | ------------------\nHash | `{}`    | 2.x, 3.x, 4.x, 5.x\n\nA hash of default facts that should be used for all the tests.\n\n#### hiera\\_config\nType   | Default       | Puppet Version(s)\n------ | ------------- | -----------------\nString | `\"/dev/null\"` | 3.x, 4.x, 5.x\n\nThe path to your `hiera.yaml` file (if used).\n\n#### default\\_node\\_params\nType | Default | Puppet Version(s)\n---- | ------- | -----------------\nHash | `{}`    | 4.x, 5.x\n\nA hash of default node parameters that should be used for all the tests.\n\n#### default\\_trusted\\_facts\nType | Default | Puppet Version(s)\n---- | ------- | -----------------\nHash | `{}`    | 4.x, 5.x\n\nA hash of default trusted facts that should be used for all the tests\n(available in the manifests as the `$trusted` hash). In order to use this, the\n`trusted_node_data` setting must be set to `true`.\n\n#### trusted\\_node\\_data\nType    | Default | Puppet Version(s)\n------- | ------- | -----------------\nBoolean | `false` | \u003e=3.4, 4.x, 5.x\n\nConfigures rspec-puppet to use the `$trusted` hash when compiling the\ncatalogues.\n\n#### trusted\\_server\\_facts\nType    | Default | Puppet Version(s)\n------- | ------- | -----------------\nBoolean | `false` | \u003e=4.3, 5.x\n\nConfigures rspec-puppet to use the `$server_facts` hash when compiling the\ncatalogues.\n\n#### confdir\nType   | Default         | Puppet Version(s)\n------ | --------------- | ------------------\nString | `\"/etc/puppet\"` | 2.x, 3.x, 4.x, 5.x\n\nThe path to the main Puppet configuration directory.\n\n#### config\nType   | Default                | Puppet Version(s)\n------ | ---------------------- | ------------------\nString | Puppet's default value | 2.x, 3.x, 4.x, 5.x\n\nThe path to `puppet.conf`.\n\n#### manifest\nType   | Default                | Puppet Version(s)\n------ | ---------------------- | -----------------\nString | Puppet's default value | 2.x, 3.x\n\nThe entry-point manifest for Puppet, usually `$manifest_dir/site.pp`.\n\n#### template\\_dir\nType   | Default | Puppet Version(s)\n------ | ------- | -----------------\nString | `nil`   | 2.x, 3.x\n\nThe path to the directory that Puppet should search for templates that are\nstored outside of modules.\n\n#### environmentpath\nType   | Default                               | Puppet Version(s)\n------ | ------------------------------------- | -----------------\nString | `\"/etc/puppetlabs/code/environments\"` | 4.x, 5.x\n\nThe search path for environment directories.\n\n#### parser\nType   | Default     | Puppet Version(s)\n------ | ----------- | -----------------\nString | `\"current\"` | \u003e= 3.2\n\nThis switches between the 3.x (`current`) and 4.x (`future`) parsers.\n\n#### ordering\nType   | Default        | Puppet Version(s)\n------ | -------------- | -----------------\nString | `\"title-hash\"` | \u003e= 3.3, 4.x, 5.x\n\nHow unrelated resources should be ordered when applying a catalogue.\n * `manifest` - Use the order in which the resources are declared in the\n   manifest.\n * `title-hash` - Order the resources randomly, but in a consistent manner\n   across runs (the order will only change if the manifest changes).\n * `random` - Order the resources randomly.\n\n#### strict\\_variables\nType    | Default | Puppet Version(s)\n------- | ------- | -----------------\nBoolean | `false` | \u003e= 3.5, 4.x, 5.x\n\nMakes Puppet raise an error when it tries to reference a variable that hasn't\nbeen defined (not including variables that have been explicitly set to\n`undef`).\n\n#### stringify\\_facts\nType    | Default | Puppet Version(s)\n------- | ------- | -----------------\nBoolean | `true`  | \u003e= 3.3, 4.x, 5.x\n\nMakes rspec-puppet coerce all the fact values into strings (matching the\nbehaviour of older versions of Puppet).\n\n#### enable\\_pathname\\_stubbing\nType    | Default | Puppet Version(s)\n------- | ------- | ------------------\nBoolean |`false`  | 2.x, 3.x, 4.x, 5.x\n\nConfigures rspec-puppet to stub out `Pathname#absolute?` with it's own\nimplementation. This should only be enabled if you're running into an issue\nrunning cross-platform tests where you have Ruby code (types, providers,\nfunctions, etc) that use `Pathname#absolute?`.\n\n#### setup\\_fixtures\nType    | Default | Puppet Version(s)\n------- | ------- | ------------------\nBoolean | `true`  | 2.x, 3.x, 4.x, 5.x\n\nConfigures rspec-puppet to automatically create a link from the root of your\nmodule to `spec/fixtures/\u003cmodule name\u003e` at the beginning of the test run.\n\n#### derive\\_node\\_facts\\_from\\_nodename\nType    | Default | Puppet Version(s)\n------- | ------- | -----------------\nBoolean | `true`  | 2.x, 3.x, 4.x, 5.x\n\nIf `true`, rspec-puppet will override the `fdqn`, `hostname`, and `domain`\nfacts with values that it derives from the node name (specified with\n`let(:node)`.\n\nIn some circumstances (e.g. where your nodename/certname is not the same as\nyour FQDN), this behaviour is undesirable and can be disabled by changing this\nsetting to `false`.\n\n## Naming conventions\n\nFor clarity and consistency, I recommend that you use the following directory\nstructure and naming convention.\n\n    module/\n      ├── manifests/\n      ├── lib/\n      └── spec/\n           ├── spec_helper.rb\n           │\n           ├── classes/\n           │     └── \u003cclass_name\u003e_spec.rb\n           │\n           ├── defines/\n           │     └── \u003cdefine_name\u003e_spec.rb\n           │\n           ├── applications/\n           │     └── \u003capplication_name\u003e_spec.rb\n           │\n           ├── functions/\n           │     └── \u003cfunction_name\u003e_spec.rb\n           │\n           ├── types/\n           │     └── \u003ctype_name\u003e_spec.rb\n           │\n           ├── type_aliases/\n           │     └── \u003ctype_alias_name\u003e_spec.rb\n           │\n           └── hosts/\n                 └── \u003chost_name\u003e_spec.rb\n\n## Example groups\n\nIf you use the above directory structure, your examples will automatically be\nplaced in the correct groups and have access to the custom matchers.  *If you\nchoose not to*, you can force the examples into the required groups as follows.\n\n```ruby\ndescribe 'myclass', :type =\u003e :class do\n  ...\nend\n\ndescribe 'mydefine', :type =\u003e :define do\n  ...\nend\n\ndescribe 'myapplication', :type =\u003e :application do\n  ...\nend\n\ndescribe 'myfunction', :type =\u003e :puppet_function do\n  ...\nend\n\ndescribe 'mytype', :type =\u003e :type do\n  ...\nend\n\ndescribe 'My::TypeAlias', :type =\u003e :type_alias do\n  ...\nend\n\ndescribe 'myhost.example.com', :type =\u003e :host do\n  ...\nend\n```\n\n## Defined Types, Classes \u0026 Applications\n\n### Matchers\n\n#### Checking if the catalog compiles\n\nYou can test whether the subject catalog compiles cleanly with `compile`.\n\n```ruby\nit { is_expected.to compile }\n```\n\nTo check the error messages of your class, you can check for raised error messages.\n\n```ruby\nit { is_expected.to compile.and_raise_error(/error message match/) }\n```\n\n#### Checking if a resource exists\n\nYou can test if a resource exists in the catalogue with the generic\n`contain_\u003cresource type\u003e` matcher.\n\n```ruby\nit { is_expected.to contain_augeas('bleh') }\n```\n\nYou can also test if a class has been included in the catalogue with the\nsame matcher.\n\n```ruby\nit { is_expected.to contain_class('foo') }\n```\n\nNote 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.\n\nIf your resource type includes :: (e.g.\n`foo::bar` simply replace the :: with __ (two underscores).\n\n```ruby\nit { is_expected.to contain_foo__bar('baz') }\n```\n\nYou can further test the parameters that have been passed to the resources with\nthe generic `with_\u003cparameter\u003e` chains.\n\n```ruby\nit { is_expected.to contain_package('mysql-server').with_ensure('present') }\n```\n\nIf you want to specify that the given parameters should be the only ones passed\nto the resource, use the `only_with_\u003cparameter\u003e` chains.\n\n```ruby\nit { is_expected.to contain_package('httpd').only_with_ensure('latest') }\n```\n\nYou can use the `with` method to verify the value of multiple parameters.\n\n```ruby\nit do\n  is_expected.to contain_service('keystone').with(\n    'ensure'     =\u003e 'running',\n    'enable'     =\u003e 'true',\n    'hasstatus'  =\u003e 'true',\n    'hasrestart' =\u003e 'true'\n  )\nend\n```\n\nThe same holds for the `only_with` method, which in addition verifies the exact\nset of parameters and values for the resource in the catalogue.\n\n```ruby\nit do\n  is_expected.to contain_user('luke').only_with(\n    'ensure' =\u003e 'present',\n    'uid'    =\u003e '501'\n  )\nend\n```\n\nYou can also test that specific parameters have been left undefined with the\ngeneric `without_\u003cparameter\u003e` chains.\n\n```ruby\nit { is_expected.to contain_file('/foo/bar').without_mode }\n```\n\nYou can use the without method to verify that a list of parameters have not been\ndefined\n\n```ruby\nit { is_expected.to contain_service('keystone').without(\n  ['restart', 'status']\n)}\n```\n\n#### Checking the number of resources\n\nYou can test the number of resources in the catalogue with the\n`have_resource_count` matcher.\n\n```ruby\nit { is_expected.to have_resource_count(2) }\n```\n\nThe number of classes in the catalogue can be checked with the\n`have_class_count` matcher.\n\n```ruby\nit { is_expected.to have_class_count(2) }\n```\n\nYou can also test the number of a specific resource type, by using the generic\n`have_\u003cresource type\u003e_resource_count` matcher.\n\n```ruby\nit { is_expected.to have_exec_resource_count(1) }\n```\n\nThis last matcher also works for defined types. If the resource type contains\n::, you can replace it with __ (two underscores).\n\n```ruby\nit { is_expected.to have_logrotate__rule_resource_count(3) }\n```\n\n*NOTE*: when testing a class, the catalogue generated will always contain at\nleast one class, the class under test. The same holds for defined types, the\ncatalogue generated when testing a defined type will have at least one resource\n(the defined type itself).\n\n#### Relationship matchers\n\nThe 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 (**-\u003e**, **~\u003e**, **\u003c-** and **\u003c~**), they’re all tested the same.\n\n```ruby\nit { is_expected.to contain_file('foo').that_requires('File[bar]') }\nit { is_expected.to contain_file('foo').that_comes_before('File[bar]') }\nit { is_expected.to contain_file('foo').that_notifies('File[bar]') }\nit { is_expected.to contain_file('foo').that_subscribes_to('File[bar]') }\n```\n\nAn array can be used to test a resource for multiple relationships\n\n```ruby\nit { is_expected.to contain_file('foo').that_requires(['File[bar]', 'File[baz]']) }\nit { is_expected.to contain_file('foo').that_comes_before(['File[bar]','File[baz]']) }\nit { is_expected.to contain_file('foo').that_notifies(['File[bar]', 'File[baz]']) }\nit { is_expected.to contain_file('foo').that_subscribes_to(['File[bar]', 'File[baz]']) }\n```\n\nYou can also test the reverse direction of the relationship, so if you have the following bit of Puppet code\n\n```ruby\nnotify { 'foo': }\nnotify { 'bar':\n  before =\u003e Notify['foo'],\n}\n```\n\nYou can test that **Notify[bar]** comes before **Notify[foo]**\n\n```ruby\nit { is_expected.to contain_notify('bar').that_comes_before('Notify[foo]') }\n```\nOr, you can test that **Notify[foo]** requires **Notify[bar]**\n\n```ruby\nit { is_expected.to contain_notify('foo').that_requires('Notify[bar]') }\n```\n\n##### Match target syntax\n\nNote 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.\n\nThese work\n* `Notify[foo]`\n* `Class[profile::apache]`\n\nThese will not work\n* `Notify['foo']`\n* `Notify[foo, bar]`\n* `Class[::profile::apache]`\n\n##### Recursive dependencies\n\nThe relationship matchers are recursive in two directions:\n\n* vertical recursion, which checks for dependencies with parents of the resource\n (i.e. the resource is contained, directly or not, in the class involved in the relationship).\n E.g. where `Package['foo']` comes before `File['/foo']`:\n\n```puppet\nclass { 'foo::install': } -\u003e\nclass { 'foo::config': }\n\nclass foo::install {\n  package { 'foo': }\n}\n\nclass foo::config {\n  file { '/foo': }\n}\n```\n\n* horizontal recursion, which follows indirect dependencies (dependencies of dependencies).\n E.g. where `Yumrepo['foo']` comes before `File['/foo']`:\n\n```puppet\nclass { 'foo::repo': } -\u003e\nclass { 'foo::install': } -\u003e\nclass { 'foo::config': }\n\nclass foo::repo {\n  yumrepo { 'foo': }\n}\n\nclass foo::install {\n  package { 'foo': }\n}\n\nclass foo::config {\n  file { '/foo': }\n}\n```\n\n##### Autorequires\n\nAutorequires are considered in dependency checks.\n\n#### Type matcher\n\nWhen testing custom types, the `be_valid_type` matcher provides a range of expectations:\n\n* `with_provider(\u003cprovider_name\u003e)`: check that the right provider was selected\n* `with_properties(\u003cproperty_list\u003e)`: check that the specified properties are available\n* `with_parameters(\u003cparameter_list\u003e)`: check that the specified parameters are available\n* `with_features(\u003cfeature_list\u003e)`: check that the specified features are available\n* `with_set_attributes(\u003cparam_value_hash\u003e)`: check that the specified attributes are set\n\n#### Type alias matchers\n\nWhen testing type aliases, the `allow_value` and `allow_values` matchers are used to check if the\nalias accepts particular values or not:\n\n\n```ruby\ndescribe 'MyModule::Shape' do\n  it { is_expected.to allow_value('square') }\n  it { is_expected.to allow_values('circle', 'triangle') }\n  it { is_expected.not_to allow_value('blue') }\nend\n```\n\n### Writing tests\n\n#### Basic test structure\n\nTo test that\n\n    sysctl { 'baz'\n      value =\u003e 'foo',\n    }\n\nWill cause the following resource to be in included in catalogue for a host\n\n    exec { 'sysctl/reload':\n      command =\u003e '/sbin/sysctl -p /etc/sysctl.conf',\n    }\n\nWe can write the following testcase (in `spec/defines/sysctl_spec.rb`)\n\n```ruby\ndescribe 'sysctl' do\n  let(:title) { 'baz' }\n  let(:params) { { 'value' =\u003e 'foo' } }\n\n  it { is_expected.to contain_exec('sysctl/reload').with_command(\"/sbin/sysctl -p /etc/sysctl.conf\") }\nend\n```\n\n#### Specifying the title of a resource\n\n```ruby\nlet(:title) { 'foo' }\n```\n\n#### Specifying the parameters to pass to a resources or parameterised class\n\nParameters of a defined type, class or application can be passed defining `:params` in a let,\nand passing it a hash as seen below.\n\n```ruby\nlet(:params) { {'ensure' =\u003e 'present', ...} }\n```\n\nFor passing Puppet's `undef` as a paremeter value, you can simply use `:undef` and it will\nbe translated to `undef` when compiling. For example:\n\n```ruby\nlet(:params) { {'user' =\u003e :undef, ...} }\n```\n\nFor references to nodes or resources as seen when using `require` or `before` properties,\nor an `application` resource you can pass the string as an argument to the `ref` helper:\n\n```ruby\nlet(:params) { 'require' =\u003e ref('Package', 'sudoku') }\n```\n\nWhich translates to:\n\n```puppet\nmydefine { 'mytitle': require =\u003e Package['sudoku'] }\n```\n\nAnother example, for an application setup (when using `app_management`):\n\n```ruby\nlet(:params) { { 'nodes' =\u003e { ref('Node', 'dbnode') =\u003e ref('Myapp::Mycomponent', 'myapp') } } }\n```\n\nWill translate to:\n\n```puppet\nsite { myapp { 'myimpl': nodes =\u003e { Node['dbnode'] =\u003e Myapp::Mycomponent['myimpl'] } } }\n```\n\n#### Specifying the FQDN of the test node\n\nIf the manifest you're testing expects to run on host with a particular name,\nyou can specify this as follows\n\n```ruby\nlet(:node) { 'testhost.example.com' }\n```\n\n#### Specifying the environment name\n\nIf the manifest you're testing expects to evaluate the environment name,\nyou can specify this as follows\n\n```ruby\nlet(:environment) { 'production' }\n```\n\n#### Specifying the facts that should be available to your manifest\n\nBy default, the test environment contains no facts for your manifest to use.\nYou can set them with a hash\n\n```ruby\nlet(:facts) { {'operatingsystem' =\u003e 'Debian', 'kernel' =\u003e 'Linux', ...} }\n```\n\nFacts may be expressed as a value (shown in the previous example) or a structure.  Fact keys\nmay be expressed as either symbols or strings.  A key will be converted to a lower case\nstring to align with the Facter standard\n\n```ruby\nlet(:facts) { {'os' =\u003e { 'family' =\u003e 'RedHat', 'release' =\u003e { 'major' =\u003e '7', 'minor' =\u003e '1', 'full' =\u003e '7.1.1503' } } } }\n```\n\nYou can also create a set of default facts provided to all specs in your spec_helper:\n\n``` ruby\nRSpec.configure do |c|\n  c.default_facts = {\n    'operatingsystem' =\u003e 'Ubuntu'\n  }\nend\n```\n\nAny facts you provide with `let(:facts)` in a spec will automatically be merged on top\nof the default facts.\n\n#### Specifying top-scope variables that should be available to your manifest\n\nYou can create top-scope variables much in the same way as an ENC.\n\n\n```ruby\nlet(:node_params) { { 'hostgroup' =\u003e 'webservers', 'rack' =\u003e 'KK04', 'status' =\u003e 'maintenance' } }\n```\n\nYou can also create a set of default top-scope variables provided to all specs in your spec_helper:\n\n``` ruby\nRSpec.configure do |c|\n  c.default_node_params = {\n    'owner'  =\u003e 'itprod',\n    'site'   =\u003e 'ams4',\n    'status' =\u003e 'live'\n  }\nend\n```\n\n**NOTE** Setting top-scope variables is not supported in Puppet \u003c 3.0.\n\n#### Specifying extra code to load (pre-conditions)\n\nIf the manifest being tested relies on another class or variables to be set, these can be added via\na pre-condition. This code will be evaluated before the tested class.\n\n```ruby\nlet(:pre_condition) { 'include other_class' }\n```\n\nThis may be useful when testing classes that are modular, e.g. testing `apache::mod::foo` which\nrelies on a top-level `apache` class being included first.\n\nThe value may be a raw string to be inserted into the Puppet manifest, or an array of strings\n(manifest fragments) that will be concatenated.\n\n#### Specifying extra code to load (post-conditions)\n\nIn some cases, you may need to ensure that the code that you are testing comes\n**before** another set of code. Similar to the `:pre_condition` hook, you can add\na `:post_condition` hook that will ensure that the added code is evaluated\n**after** the tested class.\n\n```ruby\nlet(:post_condition) { 'include other_class' }\n```\n\nThis may be useful when testing classes that are modular, e.g. testing class\n`do_strange_things::to_the_catalog` which must come before class ``foo``.\n\nThe value may be a raw string to be inserted into the Puppet manifest, or an\narray of strings (manifest fragments) that will be concatenated.\n\n#### Specifying the path to find your modules\n\nI recommend setting a default module path by adding the following code to your\n`spec_helper.rb`\n\n```ruby\nRSpec.configure do |c|\n  c.module_path = '/path/to/your/module/dir'\nend\n```\n\nHowever, if you want to specify it in each example, you can do so\n\n```ruby\nlet(:module_path) { '/path/to/your/module/dir' }\n```\n\n#### Specifying trusted facts\n\nWhen testing with Puppet \u003e= 4.3 the trusted facts hash will have the standard trusted fact keys\n(certname, domain, and hostname) populated based on the node name (as set with `:node`).\n\nBy default, the test environment contains no custom trusted facts (as usually obtained\nfrom certificate extensions) and found in the `extensions` key. If you need to test against\nspecific custom certificate extensions you can set those with a hash. The hash will then\nbe available in `$trusted['extensions']`\n\n```ruby\nlet(:trusted_facts) { {'pp_uuid' =\u003e 'ED803750-E3C7-44F5-BB08-41A04433FE2E', '1.3.6.1.4.1.34380.1.2.1' =\u003e 'ssl-termination'} }\n```\n\nYou can also create a set of default certificate extensions provided to all specs in your spec_helper:\n\n```ruby\nRSpec.configure do |c|\n  c.default_trusted_facts = {\n    'pp_uuid'                 =\u003e 'ED803750-E3C7-44F5-BB08-41A04433FE2E',\n    '1.3.6.1.4.1.34380.1.2.1' =\u003e 'ssl-termination'\n  }\nend\n```\n\n#### Specifying trusted external data\n\nWhen testing with Puppet \u003e= 6.14, the trusted facts hash will have an additional `external`\nkey for trusted external data.\n\nBy default, the test environment contains no trusted external data (as usually obtained from\ntrusted external commands and found in the `external` key). If you need to test against specific\ntrusted external data you can set those with a hash. The hash will then be available in\n`$trusted['external']`\n\n```ruby\nlet(:trusted_external_data) { {'foo' =\u003e 'bar'} }\n```\n\nYou can also create a set of default trusted external data provided to all specs in your spec_helper:\n\n```ruby\nRSpec.configure do |c|\n  c.default_trusted_external_data = {\n    'foo' =\u003e 'bar'\n  }\nend\n```\n\n#### Testing Exported Resources\n\nYou can test if a resource was exported from the catalogue by using the\n`exported_resources` accessor in combination with any of the standard matchers.\n\nYou can use `exported_resources` as the subject of a child context:\n\n```ruby\ncontext 'exported resources' do\n  subject { exported_resources }\n\n  it { is_expected.to contain_file('foo') }\nend\n```\n\nYou can also use `exported_resources` directly in a test:\n\n```ruby\nit { expect(exported_resources).to contain_file('foo') }\n```\n\n#### Testing applications\n\nApplications in some ways behave as defined resources, but are more complex so\nrequire a number of elements already documented above to be combined for testing.\n\nA full example of the simplest rspec test for a single component application:\n\n```ruby\nrequire 'spec_helper'\n\ndescribe 'orch_app' do\n  let(:node) { 'my_node' }\n  let(:title) { 'my_awesome_app' }\n  let(:params) do\n    {\n      'nodes' =\u003e {\n        ref('Node', node) =\u003e ref('Orch_app::Db', title),\n      }\n    }\n  end\n\n  it { should compile }\n  it { should contain_orch_app(title) }\nend\n```\n\nEach piece is required:\n\n* You must turn on app_management during testing for the handling to work\n* The `:node` definition is required to be set so later on you can reference it in the `:nodes` argument within `:params`\n* Applications act like defined resources, and each require a `:title` to be defined\n* The `:nodes` key in `:params` requires the use of node reference mappings to resource\n  mappings. The `ref` keyword allows you to provide these (a normal string will not work).\n\nBeyond these requirements, the very basic `should compile` test and other matchers\nas you would expect will work the same as classes and defined resources.\n\n**Note:** for the moment, cross-node support is not available and will return an error.\nEnsure you model your tests to be single-node for the time being.\n\n## Functions\n\n### Matchers\n\nAll of the standard RSpec matchers are available for you to use when testing\nPuppet functions.\n\n```ruby\nit 'should be able to do something' do\n  subject.execute('foo') == 'bar'\nend\n```\n\nFor your convenience though, a `run` matcher exists to provide easier to\nunderstand test cases.\n\n```ruby\nit { is_expected.to run.with_params('foo').and_return('bar') }\n```\n\n### Writing tests\n\n#### Basic test structure\n\n```ruby\nrequire 'spec_helper'\n\ndescribe '\u003cfunction name\u003e' do\n  ...\nend\n```\n\n#### Specifying the name of the function to test\n\nThe name of the function must be provided in the top level description, e.g.\n\n```ruby\ndescribe 'split' do\n```\n\n#### Specifying the arguments to pass to the function\n\nYou can specify the arguments to pass to your function during the test(s) using\neither the `with_params` chain method in the `run` matcher\n\n```ruby\nit { is_expected.to run.with_params('foo', 'bar', ['baz']) }\n```\n\nOr by using the `execute` method on the subject directly\n\n```ruby\nit 'something' do\n  subject.execute('foo', 'bar', ['baz'])\nend\n```\n\n#### Passing lambdas to the function\n\nA lambda (block) can be passed to functions that support either a required or\noptional lambda by passing a block to the `with_lambda` chain method in the\n`run` matcher.\n\n```ruby\nit { is_expected.to run.with_lambda { |x| x * 2 }\n```\n\n#### Testing the results of the function\n\nYou can test the result of a function (if it produces one) using either the\n`and_returns` chain method in the `run` matcher\n\n```ruby\nit { is_expected.to run.with_params('foo').and_return('bar') }\n```\n\nOr by using any of the existing RSpec matchers on the subject directly\n\n```ruby\nit 'something' do\n  subject.execute('foo') == 'bar'\n  subject.execute('baz').should be_an Array\nend\n```\n\n#### Testing the errors thrown by the function\n\nYou can test whether the function throws an exception using either the\n`and_raises_error` chain method in the `run` matcher\n\n```ruby\nit { is_expected.to run.with_params('a', 'b').and_raise_error(Puppet::ParseError) }\nit { is_expected.not_to run.with_params('a').and_raise_error(Puppet::ParseError) }\n```\n\nOr by using the existing `raises_error` RSpec matcher\n\n```ruby\nit 'something' do\n  expect { subject.execute('a', 'b') }.should raise_error(Puppet::ParseError)\n  expect { subject.execute('a') }.should_not raise_error(Puppet::ParseError)\nend\n```\n\n#### Accessing the parser scope where the function is running\n\nSome complex functions require access to the current parser's scope, e.g. for\nstubbing other parts of the system.\n\n```ruby\nbefore(:each) { scope.expects(:lookupvar).with('some_variable').returns('some_value') }\nit { is_expected.to run.with_params('...').and_return('...') }\n```\n\nNote that this does not work when testing manifests which use custom functions. Instead,\nyou'll need to create a replacement function directly.\n\n```ruby\nbefore(:each) do\n    Puppet::Parser::Functions.newfunction(:custom_function, :type =\u003e :rvalue) { |args|\n        raise ArgumentError, 'expected foobar' unless args[0] == 'foobar'\n        'expected value'\n    }\nend\n\n```\n\n\n## Hiera integration\n\n### Configuration\n\nSet the hiera config symbol properly in your spec files:\n\n```ruby\nlet(:hiera_config) { 'spec/fixtures/hiera/hiera.yaml' }\nhiera = Hiera.new(:config =\u003e 'spec/fixtures/hiera/hiera.yaml')\n```\n\nCreate your spec hiera files\n\nspec/fixtures/hiera/hiera.yaml\n```ruby\n---\n:backends:\n  - yaml\n:hierarchy:\n  - test\n:yaml:\n  :datadir: 'spec/fixtures/hiera'\n```\n\nspec/fixtures/hiera/test.yaml\n```ruby\n---\nntpserver: ['ntp1.domain.com','ntpXX.domain.com']\nuser:\n  oneuser:\n    shell: '/bin/bash'\n  twouser:\n    shell: '/sbin/nologin'\n```\n\n### Use hiera in your tests\n\n```ruby\n  ntpserver = hiera.lookup('ntpserver', nil, nil)\n  let(:params) { 'ntpserver' =\u003e ntpserver }\n```\n\n### Enabling hiera lookups\nIf you just want to fetch values from hiera (e.g. because\nyou're testing code that uses explicit hiera lookups) just specify\nthe path to the hiera config in your `spec_helper.rb`\n\n```ruby\nRSpec.configure do |c|\n  c.hiera_config = 'spec/fixtures/hiera/hiera.yaml'\nend\n```\n\nspec/fixtures/hiera/hiera.yaml\n```yaml\n---\n:backends:\n  - yaml\n:yaml:\n  :datadir: spec/fixtures/hieradata\n:hierarchy:\n  - common\n```\n\n**Please note:** In-module hiera data depends on having a correct metadata.json file. It is\nstrongly recommended that you use [metadata-json-lint](https://github.com/voxpupuli/metadata-json-lint)\nto automatically check your metadata.json file before running rspec.\n\n## Producing coverage reports\n\nYou can output a basic resource coverage report with the following in\nyour `spec_helper.rb`\n\n```ruby\nRSpec.configure do |c|\n  c.after(:suite) do\n    RSpec::Puppet::Coverage.report!\n  end\nend\n```\n\nThis checks which Puppet resources have been explicitly checked as part\nof the current test run and outputs both a coverage percentage and a\nlist of untouched resources.\n\nA desired code coverage level can be provided. If this level is not achieved, a test failure will be raised.  This can be used with a CI service, such as Jenkins or Bamboo, to enforce code coverage.  The following example requires the code coverage to be at least 95%.\n\n```ruby\nRSpec.configure do |c|\n  c.after(:suite) do\n    RSpec::Puppet::Coverage.report!(95)\n  end\nend\n```\n\nResources declared outside of the module being tested (i.e. forge dependencies)\nare automatically removed from the coverage report. There is one exception for\nthis though: **prior to Puppet 4.6.0**, resources created by functions\n(create\\_resources(), ensure\\_package(), etc) did not have the required\ninformation in them to determine which manifest they came from and so can not\nbe excluded from the coverage report.\n\n## Related projects\n\n* [puppetlabs_spec_helper](https://github.com/puppetlabs/puppetlabs_spec_helper): shared spec helpers to setup puppet\n* [rspec-puppet-augeas](https://github.com/domcleal/rspec-puppet-augeas): RSpec tests for Augeas resources inside Puppet manifests\n* [jimdo-rspec-puppet-helpers](https://github.com/Jimdo/jimdo-rspec-puppet-helpers): Tests the contents of a file with a source\n* Ease development of specs\n  * [puppet-catalog_rspec](https://github.com/enterprisemodules/puppet-catalog_rspec): Dump the Puppet Catalog as RSpec code at compile time\n  * [create_specs](https://github.com/alexharv074/create_specs.git): A different implementation that takes a compiled catalog and writes out RSpec code with various options\n* Fact providers\n  * [rspec-puppet-facts](https://github.com/mcanevet/rspec-puppet-facts): Simplify your unit tests by looping on every supported Operating System and populating facts.\n  * [rspec-puppet-osmash](https://github.com/Aethylred/rspec-puppet-osmash): Provides Operation System hashes and validations for rspec-puppet\n  * [puppet_spec_facts](https://github.com/danieldreier/puppet_spec_facts): Gem to provide puppet fact hashes for rspec-puppet testing\n\nFor a list of other module development tools see https://puppet.community/plugins/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodjek%2Frspec-puppet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frodjek%2Frspec-puppet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodjek%2Frspec-puppet/lists"}