Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
233 views
in Technique[技术] by (71.8m points)

ruby - Rspec: mocked ENV vars are visible in examples, but not in application code

TL;DR - My ruby class can read an ENV var in real life. Rspec examples can read a mocked ENV var. But my ruby class can't read the same mocked ENV var in tests. What am I doing wrong?

The full story:

I have this ruby class that uses an (optional) ENV var to set a user's flavor, defaulting to 'vanilla':

class MyConfigger
  attr_reader :flavor

  def load_config
    @flavor = ENV['MY_FLAVOR'] || 'vanilla'
    self
  end
end

This works, as tested in IRB:

% irb -I lib -r my_configger
irb(main):001:0> MyConfigger.new.load_config.flavor
=> "vanilla"


% MY_FLAVOR=cherry irb -I lib -r my_configger
irb(main):001:0> MyConfigger.new.load_config.flavor
=> "cherry"

However, it does not see the mocked ENV var when I run tests on it. The first two pass, as expected, but the last one fails, indicating that my app code is not seeing the mocked ENV var:

RSpec.describe MyConfigger do
  let(:config) { described_class.new }

  before { config.load_config }

  describe '.flavor' do
    subject { config.flavor }

    context 'with defaults' do
      it { is_expected.to eq 'vanilla' }
    end

    context 'when MY_ENV=chocolate' do
      before { allow(ENV).to receive(:[]).with('MY_FLAVOR').and_return('chocolate') }

      it "ENV['MY_FLAVOR'] in example is chocolate" do # This test passes.
        expect(ENV['MY_FLAVOR']).to eq 'chocolate'
      end

      it 'config.flavor is chocolate' do  #  <<---------------- THIS TEST FAILS
        is_expected.to eq 'chocolate'
      end
    end
  end
end
MyConfigger
  .flavor
    with defaults
      is expected to eq "vanilla"
    when MY_ENV=chocolate
      ENV['MY_FLAVOR'] in example is chocolate
      config.flavor is chocolate (FAILED - 1)

Failures:

  1) MyConfigger.flavor when MY_ENV=chocolate config.flavor is chocolate
     Failure/Error: is_expected.to eq 'chocolate'

       expected: "chocolate"
            got: "vanilla"

       (compared using ==)
     # ./spec/lib/my_configger_fail_spec.rb:25:in `block (4 levels) in <top (required)>'

3 examples, 1 failure

I've tried many variations of ENV['MY_FLAVOR'] vs ENV.fetch('MY_FLAVOR', 'vanilla') etc, and none succeeds.

What am I missing?

question from:https://stackoverflow.com/questions/66057369/rspec-mocked-env-vars-are-visible-in-examples-but-not-in-application-code

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This problem has NOTHING to do with ENV vars, and little to do with mocking. It really boils down to order of execution of before blocks in rspec.

After much hair-pulling and asking for a second set of eyes from a coworker, we realized the allow(ENV)... mock in the deep before block was being executed after the before block at the top of the test suite.

This is a bit counterintuitive (to me at least) since we're so used to deep let assignments overriding upper let assignments.

In the end, I moved the allow command up to the initial before block, to ensure that it runs before load_config gets called. (This required using a variable for the mocked value, so it doesn't break the first test for the default 'vanilla' value.)

RSpec.describe MyConfigger do
  let(:config) { described_class.new }
  let(:flavor) { nil }

  before {
    allow(ENV).to receive(:[]).with('MY_FLAVOR').and_return(flavor) #   <<------ MOVED UP HERE
    config.load_config
  }

  describe '.flavor' do
    subject { config.flavor }

    context 'with defaults' do
      it { is_expected.to eq 'vanilla' }
    end

    context 'when MY_ENV=chocolate' do
      let(:flavor) { 'chocolate' }

      it "ENV['MY_FLAVOR'] in example is chocolate" do 
        expect(ENV['MY_FLAVOR']).to eq 'chocolate'
      end

      it 'config.flavor is chocolate' do  # This test now passes! :-)
        is_expected.to eq 'chocolate'
      end
    end
  end
end

Now, I have happy tests:

MyConfigger
  .flavor
    with defaults
      is expected to eq "vanilla"
    when MY_ENV=chocolate
      ENV['MY_FLAVOR'] in example is chocolate
      config.flavor is chocolate

3 examples, 0 failures

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...