The Alchemist
The Alchemist

Reputation: 3425

Best way to mock methods to main:Object?

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().

Upvotes: 0

Views: 71

Answers (1)

Merouane Amqor
Merouane Amqor

Reputation: 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.

Upvotes: -1

Related Questions