georgthebub
georgthebub

Reputation: 407

ChefSpec test breaking with rvm include

So I'm trying to write a unit test (via ChefSpec) for one of my Chef recipes but I am getting some odd behaviour.

My recipe in question is including the rvm::system_install recipe and apparently this is causing issues with ChefSpec:

Failures:

1) theoracle::prereqs installs libyaml libyaml-devel sqlite-devel
 Failure/Error: let(:chef_run) { ChefSpec::Runner.new.converge('theoracle::prereqs')}
 NoMethodError:
   undefined method `each' for nil:NilClass
 # /Users/jdoe/workspace/cookbooks/rvm/libraries/chef_rvm_recipe_helpers.rb:44:in `install_pkg_prereqs'
 # /Users/jdoe/workspace/cookbooks/rvm/recipes/system_install.rb:29:in `from_file'
 # ./recipes/prereqs.rb:22:in `from_file'
 # ./spec/unit/recipes/prereqs_spec.rb:6:in `block (2 levels) in <top (required)>'
 # ./spec/unit/recipes/prereqs_spec.rb:15:in `block (2 levels) in <top (required)>'

The offending code segment:

/Users/jdoe/workspace/cookbooks/rvm/libraries/chef_rvm_recipe_helpers.rb:

41:        def install_pkg_prereqs(install_now = node.recipe?("rvm::gem_package"))
42:          return if mac_with_no_homebrew
43:
44>>         node[:rvm][:install_pkgs].each do |pkg|
45:            p = package pkg do

This is how my test looks like:

require 'spec_helper'
describe 'theoracle::prereqs' do
  let(:chef_run) { ChefSpec::Runner.new.converge('theoracle::prereqs')}

  it 'installs libyaml libyaml-devel sqlite-devel' do
    %W( libyaml libyaml-devel sqlite-devel ).each do |pkg|
      expect(chef_run).to_install_package(pkg)
    end
  end
end

This is the theoracle::prereqs recipe (part of it):

%W( libyaml libyaml-devel sqlite-devel ).each do |pkg|
  package pkg do
    action :install
  end
end

include_recipe 'rvm::system_install'

Upvotes: 1

Views: 434

Answers (1)

sethvargo
sethvargo

Reputation: 26997

Here is what is happening:

node[:rvm][:install_pkgs]

is returning nil. Thus you are calling nil.each and getting the exception. Unfortunately there are a number of possibilities of why that attribute could be nil.

Dependency

In your theoracle cookbook, have you added a dependency on the rvm cookbook in your metadata.rb?

depends 'rvm'

Without this line, Chef will not load the RVM cookbook (and thus not load its attributes), causing the install_pkgs attribute to be nil.

Default Attribute (most likely)

If you look at the code for determining the value of that attribute in the chef-rvm cookbook, you can see that node[:rvm][:install_pkgs] is only set under the following conditions:

case platform
when "redhat","centos","fedora","scientific","amazon"
  node.set['rvm']['install_pkgs']   = %w{sed grep tar gzip bzip2 bash curl git}
  default['rvm']['user_home_root']  = '/home'
when "debian","ubuntu","suse"
  node.set['rvm']['install_pkgs']   = %w{sed grep tar gzip bzip2 bash curl git-core}
  default['rvm']['user_home_root']  = '/home'
when "gentoo"
  node.set['rvm']['install_pkgs']   = %w{git}
  default['rvm']['user_home_root']  = '/home'
when "mac_os_x", "mac_os_x_server"
  node.set['rvm']['install_pkgs']   = %w{git}
  default['rvm']['user_home_root']  = '/Users'
end

In the case of ChefSpec, all Ohai data (which is what sets the platform key), is mocked to allow control over this behavior. Most likely, ChefSpec is reporting its platform as chefspec, and since that case statement has no default block, the attribute is nil.

To fix this issue, tell ChefSpec to behave like a particular OS:

ChefSpec::Runner.new(platform: 'ubuntu', version: '12.04')

Or manually set the attribute in your test:

let(:chef_run) do
  ChefSpec::Runner.new do |node|
    node.set['rvm']['install_pkgs'] = %w(whatever)
  end.converge(described_recipe)
end

Upvotes: 1

Related Questions