Testing Types

Last updated: 17 August 2021

Basic Test Structure

Tests for types should be placed in files under spec/types. For example, the tests for the sudoers_entry type should be in spec/types/sudoers_entry_spec.rb.

require 'spec_helper'

describe '<type name>' do
  # tests go here
end

Configuring The Tests

Specifying parameters

If the object being tested takes parameters, these can be specified as a hash of values using let(:params).

let(:params) { {'ensure' => 'present', 'enable' => true} }

When passing undef as a parameter value, it should be passed as the symbol :undef.

let(:params) { {'user' => :undef} }

When passing a reference to a resource (e.g. Package['apache2']), it should be passed as a call to the ref helper (ref(<resource type>, <resource title>))

let(:params) { {'require' => ref('Package', 'apache2')} }

If have nested RSpec contexts to test the behaviour of different parameter values, you can partially override the parameters by merging the changed parameters into super() in your let(:params) block.

describe 'My::Class' do
  let(:params) do
    {
      'some_common_param' => 'value',
      'ensure'            => 'present',
    }
  end

  context 'with ensure => absent' do
    let(:params) do
      super().merge({ 'ensure' => 'absent' })
    end

    it { should compile }
  end
end

Specifying the FQDN of the test node

If the object being tested depends upon the node having a certain name, it can be specified using let(:node).

let(:node) { 'testhost.example.com' }

Specifying facts

By default, the test environment contains only the hostname, domain, and fqdn facts (determined by the FQDN of the test node). Additional facts can be specified as a hash of values using let(:facts).

let(:facts) { {'operatingsystem' => 'Debian', 'ipaddress' => '192.168.0.1'} }

Facts may be expressed as a value (shown in the previous example) or a structure. Fact keys may be expressed as symbols or strings, and will be converted to a lower case string to align with the Facter standard.

let(:facts) do
  {
    'os' => {
      'family'  => 'RedHat',
      'release' => {
        'major' => '7',
        'minor' => '1',
        'full'  => '7.1.1503',
      }
    }
  }
end

These facts will be merged into the default facts (if set), with these values taking precedence over the default fact values in the event of a conflict.

If have nested RSpec contexts to test the behaviour of different fact values, you can partially override the parent facts by merging the changed facts into super() in your let(:facts) block.

describe 'My::Class' do
  let(:facts) do
    {
      'operatingsystem' => 'Debian',
      'role'            => 'default',
    }
  end

  context 'with role => web' do
    let(:facts) do
      super().merge({ 'role' => 'web' })
    end

    it { should compile }
  end
end
A common pattern is to use rspec-puppet-facts to automatically populate the facts for a specified operating system. Please read the rspec-puppet-facts documentation for more information.

Specifying trusted facts

When testing with Puppet >= 4.3, the trusted facts hash will have the standard trusted facts (certname, domain, and hostname) populated based on the node name. Those elements can only be set with the let(:node), not with this structure.

By default, the test environment contains no custom trusted facts (usually obtained from certificate extensions) and found in the extensions key. If the manifest being tested depends on the values from specific custom certificate extensions, they can be specified as a hash using let(:trusted_facts).

let(:trusted_facts) { {'pp_uuid' => '012345670-ABCD', 'some' => 'value'} }

These trusted facts will be merged into the default trusted facts (if set), with these values taking precedence over the default trusted facts in the event of a conflict.

If have nested RSpec contexts to test the behaviour of different trusted fact values, you can partially override the parent trusted facts by merging the changed facts into super() in your let(:trusted_facts) block.

describe 'My::Class' do
  let(:trusted_facts) do
    {
      'some_common_param' => 'value'
      'role'              => 'default',
    }
  end

  context 'with role => web' do
    let(:trusted_facts) do
      super().merge({ 'role' => 'web' })
    end

    it { should compile }
  end
end

Testing The Type

All type testing is currently done with the be_valid_type matcher.

Testing Provider Selection

The automatic provider selection can be tested by chaining the with_provider method on to the be_valid_type matcher.

it { is_expected.to be_valid_type.with_provider('foo') }

Testing Properties

Property names can be tested by chaining the with_properties method on to the be_valid_type matcher.

it { is_expected.to be_valid_type.with_properties('ensure') }

Testing Parameters

Parameter names can be tested by chaining the with_parameters method on to the be_valid_type matcher.

it { is_expected.to be_valid_type.with_parameters('command', 'unless') }

Testing provider features

Provider features can be tested by chaining the with_features method on to the be_valid_type matcher.

it { is_expected.to be_valid_type.with_features('refreshable') }