0

We use the inspec framework a lot.

It encourages code such as the following, and we have a lot of it:

Sample Code

rock_critic/controls.rb from https://docs.chef.io/inspec/inputs/#a-simple-example:

# Set a default value for an input.  This is optional.
input('amplifier_max_volume', value: 10)

control 'Big Rock Show' do
  describe input('amplifier_max_volume') do    # This line reads the value of the input
    it { should cmp 11 } # The UK'S LOUDEST BAND
  end
end

Input File

input.yml:

amplifier_max_volume: 11

Sample Execution

This would be executed as:

inspec exec rock_critic --input-file input.yml

Issue

We would like to run this code with a variety of scenarios (e.g. list of pets from different sources, like files or created at runtime), using regular Ruby instead of the inspec wrapper. i.e., ruby rock_critic/controls.rb

But if you do that, this is what you get:

Error

$ ruby ./controls/controls.rb                                                                                                                           12:14:45
./controls/controls.rb:2:in `<main>': undefined method `input' for main:Object (NoMethodError)
$

So, perhaps I got the title or vocabulary wrong, but what's the best way to mock a method like input(), which is defined globally (main:Object)?

Ideally, we'd like to have a separate test (describe, it, etc.) and pass in a variety of inputs to input().

I've tried mixins and stuff like that, but it never quite works. Is there a good example? We're probably doing something wrong.

Hope that's a better explanation, thank you!

Hack

The hack I've been using is monkey-patching Object.

    require 'minitest/autorun'


    input_hash = {
      'amplifier_max_volume' => 12
    }


      Object.define_method(:input) { |key, **opts| input_hash[key] }

      require_relative 'rock_critic/controls.rb'

Unfortunately, this doesn't seem quite right and it's not easy to supply different values for input().

The Alchemist
  • 3,397
  • 21
  • 22
  • I see no indication that `input` is defined against `Object`. It appears to be defined for `RSpec::Core::ExampleGroup`. I am unclear on exactly what you hope to accomplish though, so if you could add a bit more detail that would be appreciated. – engineersmnky Jul 28 '23 at 13:46
  • 1
    This looks like Python and Ruby had a baby. – tadman Jul 28 '23 at 14:21
  • @engineersmnky: i added more details to the issue, hope it helps! – The Alchemist Jul 28 '23 at 16:18
  • You need `let` statements, before blocks, or shared examples. Your #input method isn't defined in your provided code, and no reason why you should expect an undefined method to yield anything but a NoMethodError. Add a test to check `defined? :input` in your test scope, and that will be the failing test that guides you towards an answer. – Todd A. Jacobs Jul 31 '23 at 02:43

1 Answers1

-1

You're facing an issue because input() is a method provided by the InSpec DSL and not a global function in Ruby. when you try to run the code with Ruby directly, it's unable to recognize input(). A possible workaround could be to define a mock method in the Kernel module or use the actual inspec command to test the real behavior of your code.