chefspec: Cannot test cross-platform cookbooks

This is something @sethvargo and I discussed privately and I thought I should open an issue here. Seth, feel free to give a more cogent explanation & close it if you feel it can’t be solved.

Basically, attempting to test cookbooks intended for one platform (e.g. Windows) on another (e.g. Mac OS X) does not seem to work, because there is no way to stub basic Kernel functionality like ‘require’.

Concrete example:

require 'spec_helper'

describe 'foo::windows_web_server' do
  let(:chef_run) { ChefSpec::Runner.new(platform: 'windows', version: '2008R2').converge(described_recipe) }
  before(:each) do
    stub_const("RUBY_PLATFORM", "windows") # you'll see what this is about below
  end
  # expectations below here don't actually matter
end

When run on a Mac, this spec fails, because the library from the Windows cookbook is loaded as part of the recipe-under-test, and the following happens:

Failures:

  1) foo::windows_web_server includes the _windows_base role
     Failure/Error: let(:chef_run) { ChefSpec::Runner.new(platform: 'windows', version: '2008R2').converge(described_recipe) }
     LoadError:
       cannot load such file -- win32/registry
     # /var/folders/56/vlf96yyx30vdx5qlq6n4pkxw0000gp/T/d20140401-74327-qv3myy/cookbooks/windows/libraries/registry_helper.rb:26:in `<top (required)>'
     # ./spec/unit/windows_web_server_spec.rb:4:in `block (2 levels) in <top (required)>'
     # ./spec/unit/windows_web_server_spec.rb:10:in `block (2 levels) in <top (required)>'

Is this just a limitation in unit testing?

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Comments: 28 (10 by maintainers)

Most upvoted comments

FWIW, here’s something that works in WSL, testing cookbooks that normally converge on Windows:

Credits to @goodrum for many of the bits in there.

describe 'my::recipe' do
  context 'When all attributes are default, on windows' do
    let(:chef_run) do
      runner = ChefSpec::SoloRunner.new(platform: 'windows', version: '10')
      runner.converge(described_recipe)
    end

    let(:shellout) do
      # Creating a double allows us to stub out the response from Mixlib::ShellOut
      double(run_command: nil, error!: nil, stdout: '', stderr: '', exitstatus: 0, live_stream: '')
    end

    before do
      # Any shell command that we're running. This can be separated into
      # specific commands if we want to, but this, at least, makes things work.
      stub_command(/.*/).and_return('success')
    end

    it 'converges successfully' do
      # First we need to allow Mixlib::Shellout to receive the method :new and then return our double 'shellout'
      allow(Mixlib::ShellOut).to receive(:new).and_return(shellout)
      # Next, we need to tell the double to receive the method :stdout and then return some custom output
      allow(shellout).to receive(:stdout).and_return('success')
      # Allow registry_key_exists to be called on non-windows
      allow_any_instance_of(Chef::DSL::RegistryHelper).to receive(:registry_key_exists?).and_return(true)

      expect { chef_run }.to_not raise_error
    end
  end
end

This is how to get around it:

let(:chef_run) do
  ChefSpec::SoloRunner.new(platform: 'windows', version: '2008R2') do
    allow_any_instance_of(Chef::DSL::RegistryHelper).to receive(:registry_key_exists?).and_return(true)
  end.converge(described_recipe)
end