0

Let's say I have a class that uses automatic hiera lookups:

class foo::file {
  file { $foo::file_name:
    owner  => $foo::owner,
    group  => $foo::group,
    ensure => $foo::ensure,
  }
}

The common.yaml for this module contains bindings for this:

foo::file_name: 'bar.txt'
foo::owner: 'user'
foo::group: 'user_group'
foo::ensure: 'present'

If I want to test this in rspec-puppet, how do I change the hiera data within the actual spec file? The only references I can find for this are in hiera-puppet-helper, which appears to be long abandoned, which looked like:

  let(:hiera_data) { { 'foo::file_name' => 'nope.txt' } }
Cory Ringdahl
  • 522
  • 5
  • 12
  • 1
    The class presented does not use any external data, at least not directly. It is possible to configure Hiera data for use by spec tests, but it is not clear to me what advantage you expect to gain from that, especially for testing the class you have actually presented. The main reason ***I*** have found for configuring Hiera data for spec tests is to feed required data to *dependencies* of the class under test, not to the class under test itself, and that's usually a convenience, not a necessity. – John Bollinger Sep 08 '22 at 17:15
  • The class is a subclass of `foo`, which contains all the specified data. We're using hiera to contain all params, and expect to have simplified data distributed amongst the subclasses. For example, `$foo::ensure` will be passed to `foo::config` and `foo::service`, instead of having separate `$foo::config::ensure` and `$foo::service::ensure`. I'm looking for inline hiera replacement instead of a mess of hiera yaml in `spec/fixtures`. – Cory Ringdahl Sep 08 '22 at 17:55
  • 1
    Does class `foo` obtain Hiera data via automatic data binding (as it probably it should) or by making explicit `hiera()` or `lookup()` calls? And how are you getting it loaded in the context of your spec tests, since class `foo::file` does not arrange for that itself? – John Bollinger Sep 08 '22 at 18:08
  • Agree with @JohnBollinger: your module should already have the necessary data, and you can vary the values for dynamic unit tests by varying the config lookup parameters, and not mocking data directly, or within extra data files in the unit test directory. – Matthew Schuchard Sep 08 '22 at 19:43
  • Side note: as presented, your class `foo::file` is **not** a subclass of class `foo`. It is merely a class in *module* `foo` (or possibly not even that), which module happens also to contain a class named `foo` (maybe). There is no subclass relationship between these two. Puppet does have class inheritance, and bona fide subclassing in that sense, but the class presented does not exercise that, as indeed it shouldn't. Puppet class inheritance is poorly regarded and should not be used, except for rather special purposes. – John Bollinger Sep 08 '22 at 20:07
  • Apologies, I used the wrong terminology. It's definitely a class in module foo, not a subclass. I'm counting on automatic data binding, as you say. Basically, I'm looking for a `let(:params)` replacement, something like the `let(:hiera_data)` that existed in hiera-puppet-helper, but now is long gone. – Cory Ringdahl Sep 08 '22 at 21:03
  • Again, how are you getting class `foo` loaded in your tests for class `foo::file`? This is key, because it is to `foo`, not to `foo::file`, to which you need to bind data. And because if you're not getting class `foo` loaded at all then that's an issue you need to deal with first. – John Bollinger Sep 08 '22 at 21:16

1 Answers1

0

If I want to test this in rspec-puppet, how do I change the hiera data within the actual spec file?

You do not want to test that, specifically, in your spec tests. You are focusing too narrowly. The binding of Hiera data to class parameters is a function of the Puppet core, not of your classes, so you should not consider it in scope for your testing. What you seem actually to want to test is various combinations of parameter values for class foo, with that not being the class under test. Once you recognize that, you should also recognize that any means of assigning the wanted class parameter values will do the job. It doesn't have to be Hiera.

And there indeed is a viable alternative to using Hiera for the purpose: you can use a precondition to provide a resource-like declaration of class foo that expresses the wanted parameter values. And you need to provide some kind of declaration anyway if, as shown in the question, class foo::file does not provide for declaring foo itself. Example:

    let(:pre_condition) do
      <<-EOS
        class {'foo':
          file_name => 'bar.txt',
          owner     => 'user',
          group     => 'user_group',
          ensure    => 'present'
        }
      EOS
    end

I should not neglect at this point to add that resource-like class declarations should usually be avoided in your manifests. But the reasons for that are much less pronounced in the narrow scope and targeted use of a spec test.

I note also that the precondition approach does not conflict with the case where the class under test directly or indirectly executes an include-like declaration of class foo, as might very well happen in real-world cases. It would conflict if the class under test provided its own resource-like declaration of class foo, but it doesn't look like that would even be viable in cases like yours.

Additionally, be aware that your module-specific Hiera data is visible to the module's spec tests. You do not need to provide for that manually. You need the pre-condition or something like that only for testing data different from or in addition to the data already provided by your module.

And note that this does not address data that are directly looked up by your classes via the lookup() function or one of the hiera*() functions. For that, you need to set up bona fide Hiera data in your test fixture to test data other than your module-specific Hiera data.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157