Best way to deal with environment-sourced constants when rspec testing?

Viewed 276

I have a model that has a constant that looks like:

  SOURCE_QUOTA = {
    'free' => (ENV['FREE_SOURCE_QUOTA'] || '5').to_i,
    'premium' => (ENV['PREMIUM_SOURCE_QUOTA'] || '100').to_i
  }.freeze

RuboCop suggests "Style/MutableConstant" and that the freeze should be there.

However, during testing, I want to use slightly different values. Now, I could actually modify the ENV of the bash session where I launch rspec. Sure. But modifying it in the code makes life easier. For example:

  describe "a thing" do
    it "does stuff" do
      ENV['FREE_SOURCE_QUOTA'] = '2'
      DataSource::SOURCE_QUOTA["free"] = ENV['FREE_SOURCE_QUOTA'].to_i
      # test code

The above works when the constant isn't frozen. I assume that's because, when frozen, the Model is loaded during the loading of the Rails environment and then this value cannot be muted (it's frozen!).

What would be the best thing to do here?

  • stick with frozen constant and be sure to set desired env before the rspec run?
  • don't freeze the constant and keep the above test
  • do something else that involves unfreezing/refreezing/reloading model?

Thanks!

2 Answers

I would think anout g a class method instead of a constant. Something like this:

# in your model
def self.source_quota
  {
    'free' => (ENV['FREE_SOURCE_QUOTA'] || '5').to_i,
    'premium' => (ENV['PREMIUM_SOURCE_QUOTA'] || '100').to_i
  }
end

and then mock that method in your tests like this:

allow(DataSource).to receive(source_quota)and_return('free' => 2)

One way is to use RSpec's stub_const feature. This allows you to stub the constant value to whatever you want in your example.

Your test code might look something like

describe 'a thing' do
  before { stub_const('DataSource::SOURCE_QUOTA', {'free' => 2}) } 
  it 'does stuff' do
    expect(DataSource::SOURCE_QUOTA["free"]).to eq 2
    # test code
Related