Using Fake Cookbooks for Writing ChefSpec Tests for your Custom Chef Resources

Featured image for sharing metadata for article

In line with my article Test-Driven Chef Cookbook Development Using ChefSpec (and a sprinkling of InSpec), I heavily unit test the Custom Resources I write with my Chef code.

Until recently, the way that I've done this is creating a new recipe within the cookbook I'm testing, purely for the purpose of unit testing the resource.

For instance, let's assume I'm in a cookbook spectat which has a resource called spectat_site. In this case, I would create a recipe _spectat_site whose purpose is purely to be used for unit testing the spectat_site resource.

However, even when I adopted the pattern, I didn't like it - it means having numerous extra recipes within the cookbook that are technically available for external consumers to use, but shouldn't be, as they're just for driving tests. Additionally, that single recipe gets quite complicated so we can pass in all combinations of parameters and actions to ensure it's thoroughly tested.

I've since moved to the approach, stolen from the line cookbook, where I can have a "fake" cookbook purely for the sake of tests. This would mean I'd have an associated fake test cookbook called spectat_site which has recipes for different aspects of testing the resource. This simplifies the logic within the resource-testing recipes, and makes intentions clearer to the reader.

Note that you do not need to have the fake cookbook named the same as the resource, I've used it in this example because, in my opinion, it makes it easier to see what the recipes are used for, but is up to you.

This fake cookbook sits in the spec/fixtures/cookbooks folder, with the same name as the resource it's testing:

# spec/fixtures/cookbooks/spectat_site/metadata.rb
name 'spectat_site'
depends 'spectat'

It also needs a depends on the cookbook we're testing, so it can then use the resources we want to test!

We can create the test within our top-level cookbook:

# spec/unit/resources/spectat_site/all_properties_spec.rb
describe 'spectat_site::all_properties' do
  let(:chef_run) do
    chef_run = 'ubuntu', version: '18.04', step_into: ['spectat_site'])

  it 'converges successfully' do
    expect(chef_run).to_not raise_error

  it 'creates the user' do
    expect(chef_run).to create_user('create the user for')
      .with(username: 'www_jvt_me')
      # ...

  it 'creates the folder structure' do
    expect(chef_run).to create_directory('create the directory for')
      .with(path: '/srv/www/')
      .with(owner: 'www_jvt_me')
      .with(recursive: true)
      # ...

  # ...

Which has the following all_properties recipe within our spectat_site fake cookbook:

# spec/fixtures/cookbooks/spectat_site/recipes/all_properties.rb
spectat_site 'create a site for' do
  site_fqdn ''
  site_type :static

Finally, to actually be able to find the new cookbook, we need to add to our Berksfile:

 source ''

+group :integration do
+  # TODO: add each new test cookbook here as needed
+  cookbook 'spectat_site', path: 'spec/fixtures/cookbooks/spectat_site'

This then makes sure that just when testing, ChefSpec will know how to use that cookbook - awesome stuff!

As mentioned, this can be a really nice pattern for reducing complexity in resource-testing recipes, as well as remove the fake recipes from being used accidentally by our cookbook consumers.

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #chef #custom-resource #chefspec.

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.