1

I'm including a custom gem in a chef cookbook. No matter what I've tried I'm get similar errors to undefined local variable or method `host'

I've tried many different variations to this.

allow(HostOpsCookbook).to receive(:host).with(:sonic.version).and_return('10.0')

Gem layout cust_gem \lib \ibus \host sonic.rb host.rb

host.rb

module Ibus
  class Host
   attr_reader :sonic

    def initialize
      extend_type
    end

    def enxtend_type
      @sonic = Ibus::Host::Sonic.new
    end

  end
end

host\sonic.rb

module Ibus
  class Host
    class Sonic

      def version
         .....
      end
  end
 end
end

Chef cookbook cookbook \libraries host_helper.rb \recipes default.rb

chef\cookbook\libraries\host_helper.rb

module HostOpsCookbook
  def host
    require_ibus_gem #Loads the gem
    @@host ||= Ibus::Host.new
  end
end

Chef::Recipe.send(:include, HostOpsCookbook) if defined?(Chef::Recipe)
Chef::Resource.send(:include, HostOpsCookbook) if defined?(Chef::Resource)
Chef::Provider.send(:include, HostOpsCookbook) if defined?(Chef::Provider)

chef\cookbook\recipes\default.rb

sonic_version = host.sonic.version

This as code works the call to the gem method works.

However I can't figure out how to stub the below in the spec tests.

host.sonic.version
debow
  • 73
  • 7
  • to make sure i understand you correctly, you wish to install a rubygem and use it within a recipe? – Mr. Oct 24 '19 at 17:08
  • Correct, the gem is installed within the chef\cookbook\libraries\host_helper.rb. There are multiple recipes that call different methods from the gem. All that works just fine, the code compiles/converges just fine. It's only during the spec tests that I'm having issues trying to mock/stub anything that calls the method from the gem. – debow Oct 29 '19 at 15:24
  • please update your question to reflect excatly what is the issue and when (you mentioned that it happens durin a test. please specify the framework that you are using. such as chefspec, inspec, etc.) – Mr. Oct 29 '19 at 18:20
  • It's chefspec, the issue is mentioned above. The exact error is `undefined local variable or method host` – debow Oct 30 '19 at 14:21

3 Answers3

1

The below code it was ended up working. The version method was nested under the Host/Module, Sonic/Class which is extend when the host object is initialized. This is all shown above.

allow_any_instance_of(HostOpsCookbook).to receive_message_chain(:host, :sonic, :version).and_return('10.0')

debow
  • 73
  • 7
0

You need to install the required rubygem into the environment ChefSpec is running in.

This will make sure that the rubygem is resolved by ChefSpec.

Mr.
  • 9,429
  • 13
  • 58
  • 82
0
allow(HostOpsCookbook).to

Is for stubbing class methods. You actually have a instance method, so you must use:

allow_any_instance_of(<Class>).to 

Your host method is defined in module, you cannot create an instance from modules. So you must stub the instance of the Chef::Recipe object, as you call host method from recipe.

allow_any_instance_of(Chef::Recipe).to 

Another problem would be stubbing sonic.version. As you try to chain stubbing, you must use rspec doubles in this case.

So the final result should be:

sonic_double = double("Sonic Double", version: "10.0")
allow_any_instance_of(Chef::Recipe).to receive(:host).and_return(sonic_double)
Draco Ater
  • 20,820
  • 8
  • 62
  • 86
  • 1
    Thanks for the suggestion Ater, the above was still giving me an issue about unknown method host. But your statement about chain stubbing led me to this which is working. `allow_any_instance_of(HostOpsCookbook).to receive_message_chain(:host, :sonic, :version).and_return('10.0')` – debow Oct 31 '19 at 16:01
  • @debow Then you should add it as answer to the question and accept it. – Draco Ater Oct 31 '19 at 20:41