jimhopp / chefspec_exploration

Figuring out chefspec https://www.relishapp.com/acrmp/chefspec/docs
58 stars 15 forks source link

How to test libraries? #2

Open jaypipes opened 12 years ago

jaypipes commented 12 years ago

Hi Jim, thanks for putting this together. Very helpful.

I'm wondering if you have information as to how to test Chef libraries? I've been struggling to figure out how to do it...

Here's something I put together that I thought might work for my little library. The library routine is dirt simple:

require "chef/search/query"

module OpenStack
  # Returns the Mash for a node that has the supplied
  # role in its run list. An optional section parameter
  # may be passed which limits the returned Mash to just
  # the section of the node mash matching the supplied key.
  #
  # If no node is found having the supplied role, nil is
  # returned.
  def get_config_by_role(role, section=nil)
    if node["roles"].include?(role)
      # If we're on a node that contains the searched-for role, just
      # return the node mash or subsection
      return section == nil ? node : node[section]
    else
      # Otherwise, let's look up the role based on the Chef environment
      # of the current node and the searched-for role
      query = "roles:#{role} AND chef_environment:#{node.chef_environment}"
      result, _, _ = Chef::Search::Query.new.search(:node, query)

      if result.length == 0
        Chef::Log.debug("Searched for role #{role} by found no nodes with that role in run list.")
        return nil
      else
        return section == nil ? result[0] : result[0][section]
      end
    end
  end
end

and here is the spec I put together:

require 'chefspec'
require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'roles'

describe OpenStack do
  before do
    @run = ChefSpec::ChefRunner.new.converge 'openstack-utils::default'
    @lib = Object.new.extend(OpenStack)
  end
  describe "#get_config_by_role" do
    it "returns nil when no such role found" do
      result = @lib.get_config_by_role("bar")
      result.should_return nil
    end
  end
end

Unfortunately, this is what I get:

rspec ./cookbooks/openstack-utils/spec/roles_spec.rb:10 # OpenStack#get_config_by_role returns nil when no such role found
jpipes@uberbox:~/repos/att-cloud/cookbook-openstack-utils$ rspec cookbooks/openstack-utils
F

Failures:

  1) OpenStack#get_config_by_role returns nil when no such role found
     Failure/Error: result = @lib.get_config_by_role("bar")
     NameError:
       undefined local variable or method `node' for #<Object:0x7f8bf164ac10>
     # ./cookbooks/openstack-utils/libraries/roles.rb:33:in `get_config_by_role'
     # ./cookbooks/openstack-utils/spec/roles_spec.rb:11

Finished in 0.01363 seconds
1 example, 1 failure

Failed examples:

rspec ./cookbooks/openstack-utils/spec/roles_spec.rb:10 # OpenStack#get_config_by_role returns nil when no such role found

Any help or pointers would be REALLY appreciated!

-jay

jimhopp commented 11 years ago

Hi Jay-

I'm not an expert on this, but I have written a bit of library code and corresponding specs. I'll show you what I've done that works.

Here's a snippet from a cookbook library that I extended to support NUMA nodes:

class Chef::ResourceDefinitionList::MongoDB

  def self.numa_node
    return false unless File.exist?("/sys/devices/system/node/node1")
    first_line = File.open("/proc/self/numa_maps", "r").gets
    first_line !~ /interleave/
  end

Here's an example that tests it:

  it 'numa_node returns true on NUMA node' do
    File.stub(:exist?).with("/sys/devices/system/node/node1").and_return(true)
    fake = File.open(FAKE_NUMA_MAPS_NO_INTERLEAVING, "w+")
    fake.puts("default")
    fake.close
    File.stub(:open).with("/proc/self/numa_maps", "r").and_return(File.new(FAKE_NUMA_MAPS_NO_INTERLEAVING, "r"))
    Chef::ResourceDefinitionList::MongoDB.numa_node.should be_true
  end

There are some node references in the library, but the node is passed in as a parameter.

According to http://wiki.opscode.com/display/chef/Libraries, you need to reference node as an instance member (@node).

Hope this helps. And glad my little repo was helpful.