The problem
I had some conditional logic I needed to test that was based on the current Rails environment. Basically something like:
if Rails.env.production?
# Do something
Elsif Rails.env.staging?
# Do something else
End
I needed to stub out the Rails.env call. I had a quick Google around but I couldn’t really find a way of doing this I was happy with.
The solution
I whipped up this function that allows you to set the Rails environment to whatever you want:
Can’t see this Gist? View it on Github!
Put the above function in your spec file or your spec_helper.rb. The function can be called as such:
Can’t see this Gist? View it on Github!
How does this work?
The Rails.env function looks like this:
Can’t see this Gist? View it on Github!
So all you’re doing is setting the cached instance variable to the environment you want.
Follow Up (June 20, 2011)
So two distinct issue have come up as a result of the above content.
Firstly, there was no ensure meaning that if an exception was raised in the middle of the stub then the environment would be changed for the remaining tests. This was a stupid mistake but, it happens, so I’ve updated the gist and my code.
Secondly, the issue of checking environment in code. The solution for this would be to have a constant in each env file that defines whatever it is you are using the env check for. So in the example I was working with each env file would have a EMAIL_TARGET constant.
You still need to test this, so the stub provided is still useful in this regard.
Mike Gehard
Jun 14, 2011
I’ve seen this type of env checking really abused in apps so please use it sparingly if at all.
Josh Nesbitt
Jun 14, 2011
I’d disagree Mike. Even if the example isn’t the greatest there’s still a legitimate use for stubbing out the env call for tests. Thanks for the post Jordan.
Darcy Laycock
Jun 14, 2011
@Mike For the most part I agree – Using feature switches and then setting based on environment are usually a better option (e.g. using system wide settings, testing based on them) – But in some situations (esp. when needing to work with badly behaving gems where code like this might not be an easy option) it’s good to have the technique, even if it is something only used very, very sparingly it has its uses.
Jarmo Pertman
Jun 14, 2011
I’d put that Rails.instance_variable_set(“@_env”, ActiveSupport::StringInquirer.new(original_env)) into ensure block to make sure that the original_env will be set back even if there’s some exceptions in block.call – e.g. the #should in that block fails :)
Jon Wood
Jun 14, 2011
Nice solution, you should probably wrap it a begin/ensure block to make sure any exceptions that are raised don’t cause the environment to change permanently.
Ken Collins
Jun 14, 2011
Why not use something like Mocha? I have things like this in my tests in a few key places.
Rails.env.stubs :production? => true
Josh Cheek
Jun 14, 2011
Shouldn’t the call to restore the environment be in an ensure block?
Josh Cheek
Jun 14, 2011
Also, it looks like it should just be `Rails.instance_variable_set(“@_env”, original_env)`, because original_env should already be an `ActiveSupport::StringInquirer`, since it was created by `ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || “development”)`
pete
Jun 14, 2011
You should really add some error checking to your function, else you can do something like this:
stub_env “production” do
raise “OMG”
end
# now my tests are running in production mode
For anybody playing along at home, the fix is easy:
def stub_env(new_env, &block)
original_env = Rails.env
Rails.instance_variable_set(“@_env”, ActiveSupport::StringInquirer.new(new_env))
block.call
ensure
Rails.instance_variable_set(“@_env”, ActiveSupport::StringInquirer.new(original_env))
end
Alexis
Jun 16, 2011
The real problem is having conditions based on Rail.env. To my mind it’s definitely a bad practice. I rather prefer to write more semantic code like:
if MyApp.send_notifications?
…
else
…
end
And you set the property in config/environments/development.rb:
MyApp.send_notifications = false
It’s much more cleaner, reliable and headache free :-)
Pingback: Twitted by gavinlaking
Henrik N
Aug 7, 2012
You could just stub it: http://stackoverflow.com/a/9119087/6962