Search code examples
unit-testingchef-infrachefspec

How do you mock a function call in chefspec for an object instance extended with another class


I am new to unit testing and chefspec. I am trying to mock/intercept a function call in a recipe from a dependent library

  • Library

    module Helper
      def do_something_useful
        return "http://example.com/file.txt"
      end
    end
    
  • Recipe

    remote_file '/save/file/here' do
      extend Helper
      source do_something_useful
    end
    

I have tried the following:

  • Chefspec

    allow_any_instance_of(Chef::Resource::RemoteFile).to receive(:do_something_useful).and_return('foobar')
    allow_any_instance_of(Chef::Resource).to receive(:do_something_useful).and_return('foobar')
    

    I have also tried mocking with a double:

    helper = double
    Helper.stub(:new).and_return(helper)
    allow(helper).to receive(:do_something_useful).and_return('foobar')
    

    This fails with uninitialized constant Helper


Solution

  • Sooooo this is a fun case where the extend is overwriting the mock method. So we can just use the extend to drive things:

    before do
      allow_any_instance_of(Chef::Resource::RemoteFile).to receive(:extend) do |this, m|
        Module.instance_method(:extend).bind(this).call(m)
        allow(this).to receive(:do_something_useful).and_return('foobar')
      end
    end
    

    This is like 800% magic and you probably shouldn't use it, but it does work in my little test environment.