Obromios
Obromios

Reputation: 16383

Stubbing key/value pair in ruby on rails ENV

I want to test the effect of the value of an ENV key on my code. I am stubbing this by using

allow(ENV).to receive(:[]).with('ADWORDS_RUN').and_return('No')

This was working until I changed the target code to include accessing another ENV key. The target code now includes the following

 def not_local_machine?
    !ENV['LOCAL_MACHINE']
 end

The test now fails in the above function with the error message

 Failure/Error: get 'home'
   ENV received :[] with unexpected arguments
     expected: ("ADWORDS_RUN")
          got: ("LOCAL_MACHINE")
    Please stub a default value first if message might be received with other args as well.

It appears that my current method of stubbing is wiping out other ENV keys. How do I stub an ENV key to avoid this problem?

Upvotes: 5

Views: 4028

Answers (4)

Kelsey Hannan
Kelsey Hannan

Reputation: 2947

For modifying ENV's in tests, Thoughtbot's climate_control gem is worth a look.

You wrap your test around ClimateControl block to control temporary changes to ENV values. Using your example:

ClimateControl.modify ADWORDS_RUN: 'No' do
  expect(AdwordsTask.new.run?).to eq(false)
end

To use with RSpec, you could define this in your spec:

def with_modified_env(options, &block)
  ClimateControl.modify(options, &block)
end

This would allow for more straightforward way to modify/stub environment values:

require 'spec_helper'

describe AdwordsTask, 'name' do
  it 'does not run adwords' do
    with_modified_env ADWORDS_RUN: 'No' do
      expect(AdwordsTask.new.run?).to eq(false)
    end
  end

  def with_modified_env(options, &block)
    ClimateControl.modify(options, &block)
  end
end

Upvotes: 0

Allison
Allison

Reputation: 2336

This is how I solved that issue:

before { allow(ENV).to receive(:[]).and_call_original }

context 'ADWORDS_RUN is No' do
  before { allow(ENV).to receive(:[]).with('ADWORDS_RUN').and_return('No') }

  [example block]
end

(Aside, I recommend using something like 'false' instead of 'No'.)

Upvotes: 3

AnoE
AnoE

Reputation: 8345

You are overriding/overwriting the [] method of ENV. The original meaning is gone completely.

Check out https://github.com/rspec/rspec-mocks and look for the chapter "Arbitrary Handling". It contains this sample code:

expect(double).to receive(:msg) do |arg|
  expect(arg.size).to eq 7
end

You should be able to adopt that for your needs... something along the lines of (untested)

dummy_env = { ADWORDS_RUN: 1, LOCAL_MACHINE: 2 }
allow(ENV).to receive(:[]) do |key|
  dummy_env[key] or raise "#{key} not expected"
end

Or if you want to keep all old ENV entries

env_clone = ENV.clone
allow... do|key|
   dummy_env[key] or env_clone[key]
end

Upvotes: -1

Mori
Mori

Reputation: 27789

You can use

stub_const 'ENV', ENV.to_h.merge('ADWORDS_RUN' => 'No')

Upvotes: 9

Related Questions