Reputation: 31464
I am testing some code that pulls its configuration from environment variables (set by Heroku config vars in production, for local development I use foreman).
What's the best way to test this kind of code with RSpec?
I came up with this:
before :each do
ENV.stub(:[]).with("AWS_ACCESS_KEY_ID").and_return("asdf")
ENV.stub(:[]).with("AWS_SECRET_ACCESS_KEY").and_return("secret")
end
If you don't need to test different values of the environment variables, I guess you could set them in spec_helper
instead.
Upvotes: 88
Views: 36992
Reputation: 5124
If you're using dotenv to setup your environment during tests but need to modify an env variable for a specific test then following approach can be useful.
A simpler method than stubbing ENV
is to replace the environment for the duration of the test, and then restore it afterwards like so:
with_environment("FOO" => "baz") do
puts ENV.fetch("FOO")
end
Using a helper like this:
module EnvironmentHelper
def with_environment(replacement_env)
original_env = ENV.to_hash
ENV.update(replacement_env)
yield
ensure
ENV.replace(original_env)
end
end
By using ensure
the original environment is restored even if the test fails.
There's a handy comparison of methods for setting & modifying environment variables during tests including stubbing the ENV
, replacing values before / after the test, and gems like ClimateControl.
Upvotes: 9
Reputation: 1084
I'd avoid ENV.stub(:[])
- it does not work if other things are using ENV
such as pry(you'll get an error about needing to stub DISABLE_PRY
).
#stub_const
works well as already pointed out.
Upvotes: 6
Reputation: 3635
This syntax works for me:
module SetEnvVariable
def set_env_var(name, value)
# Old Syntax
# ENV.stub(:[])
# ENV.stub(:[]).with(name).and_return(value)
allow(ENV).to receive(:[]) # stub a default value first if message might be received with other args as well.
allow(ENV).to receive(:[]).with(name).and_return(value)
end
end
Upvotes: 10
Reputation: 17392
You also can stub the constant:
stub_const('ENV', {'AWS_ACCESS_KEY_ID' => 'asdf'})
Or, if you still want the rest of the ENV:
stub_const('ENV', ENV.to_hash.merge('AWS_ACCESS_KEY_ID' => 'asdf'))
Upvotes: 102
Reputation: 49
You can use https://github.com/littleowllabs/stub_env to achieve this. It allows you to stub individual environment variables without stubbing all of them as your solution suggested.
Install the gem then write
before :each do
stub_env('AWS_ACCESS_KEY_ID', 'asdf')
stub_env('AWS_SECRET_ACCESS_KEY','secret')
end
Upvotes: 4
Reputation: 15205
What you want is the dotenv gem.
Running tests under foreman, as @ciastek suggests, works great when running specs from CLI. But that doesn't help me run specs with Ruby Test in Sublime Text 2. Dotenv does exactly what you, transparently.
Upvotes: 2
Reputation: 1363
As Heroku suggests, you can use Foreman's .env
file to store environment variables for development.
If you do that, you can use foreman run
to run your specs:
foreman run bundle exec rspec spec
Upvotes: 8
Reputation: 19489
That would work.
Another way would be to put a layer of indirection between your code and the environment variables, like some sort of configuration object that's easy to mock.
Upvotes: 29