What is the best way to write specs for code that depends on environment variables?
Solution 1
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.
Solution 2
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'))
Solution 3
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
Solution 4
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
Solution 5
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.
Luke Francl
I am a software developer currently living in San Francisco. My speciality is web-based applications, lately using Ruby on Rails, though I have probably written more web applications in Tcl than most people. Check out my CV if you're looking for a software engineer.
Updated on July 05, 2022Comments
-
Luke Francl almost 2 years
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. -
Andrew Marshall about 12 yearsIf this is in Rails you already have its own configuration object to add to if you want.
-
Luke Francl about 12 yearsThat is a handy tip. I did not know you could define your own configuration options so easily. Just do this in environments/*.rb:
config.my_config_value = 'value'
and it will be made available asRails.configuration.my_config_value
. -
David Tuite over 9 years
ENV
doesn't appear to have amerge
method (at least in Ruby 1.9.3). -
Fabio over 9 yearsThis is the best approach for a quick env stubs, but in second case you need
ENV.to_hash
to preserve original env. -
Tadas Sasnauskas almost 9 yearsIt does not help if you want to test outcomes of different values in the same env variable.
-
Pratik Khadloya over 8 yearsMaking the hash keys double quoted made it work for me. When i had the keys with single quotes, they got converted to :symbols. Ex: :AWS_ACCESS_KEY_ID
-
hoffmanc over 5 yearswon't work if you use e.g.,
fetch
forENV
access. -
Elliott de Launay over 2 yearsadd a couple more lines for fetch:
allow(ENV).to receive(:fetch).with(name).and_return(value)
-
Richard-Degenne about 2 yearsThis is probably the cleanest answer I've seen here. I'd use it with an
around
block for it to be completely idiomatic.