rspecchef-infrachefspec

Chef Library Testing with shell_out


I am writing a custom resource for Chef. The resource is used for setting sysctl values. I am basically using the Chef sysctl code and putting some limitations around it. We don't trust all the users at the company :P

I am trying to put most of the code inside a library helper module so I can test the code easier. I am not sure if this is best practice. Let me know if this is bad practice.

Anyway, the issue I am running into is trying to test my library code. Whenever I try to mock the shell_out command I always get the following error.

1) Sysctl::Helpers.set_sysctl_param
     Failure/Error: subject.set_sysctl_param("key1", "value1")

     NoMethodError:
       undefined method `shell_out!' for Sysctl::Helpers:Module

Library Code

module Sysctl
  module Helpers
    include Chef::Mixin::ShellOut
    def self.set_sysctl_param(key, value)
      shell_out!("sysctl -w \"#{key}=#{value}\"")
    end
  end
end

Test

require 'spec_helper'
describe Sysctl::Helpers do
  describe '.set_sysctl_param' do
    let(:shellout) { double(run_command: nil, error!: nil, stdout: '', stderr: double(empty?: true)) }
    before do
      allow(Chef::Mixin::ShellOut).to receive(:new).and_return(shellout)
    end

    it do
      subject.set_sysctl_param("key1", "value1")
      expect(:shellout).to receive(:run_command).with("sysctl -w \"key1=value1\"")
    end
  end
end

I appreciate any help or advise you can give me.

Thanks!


Solution

  • When you include a module, you add module methods as instance methods. But you try to access shell_out in a class method. You actually need to extend your module with Chef::Mixin::ShellOut. This way ShellOut methods will be added as class methods.

    module Sysctl
      module Helpers
        extend Chef::Mixin::ShellOut  # replace include with extend
        def self.set_sysctl_param(key, value)
          shell_out!("sysctl -w \"#{key}=#{value}\"")
        end
      end
    end
    

    More on this What is the difference between include and extend in Ruby?