One alternative proposed is changing the constant in the test where you need it like these:
And inside the test case you change the constant to what you need and restore it after the test ends:
class Module
def redefine_const(name, value)
__send__(:remove_const, name) if const_defined?(name)
const_set(name, value)
end
end
But if you can control the code that uses the constant there's a more clean way. Just wrap it in a method and get the constant through the method. During testing you just mock the method:
Object.redefine_const(:RAILS_ENV, 'production')
And then in your test you just do a normal expectation:
class A
def rails_env
RAILS_ENV
end
#...some more code that gets the constant RAILS_ENV using the method rails_env...
end
You could even have some module that wraps all useful constants in your app, so constant dependent code gets easily testable and clean.
A.any_instance.expects(:rails_env).returns 'production'





5 comments:
The way Rails is moving to methods to access constants is truly a good interface.
Also if you were in rails land, then you can use silence_warnings {} block and perhaps use a method like that that takes a block and resets the constant at the end of the block.
def redefine_const(name,value)
old_value = Object.const_get(name)
silence_warnings { Object.const_set(name,value) }
yield
ensure
Object.const_set(old_value,value)
end
Daniel, good example.
In this commit you can see Rick "technoweenie" Olson (from rails core fame) updating his very own restful_authentication plugin with a similar approach. Although he directly stops using constants in favor of class variables with accessors.
Hi Daniel,
I was struggling with testing a library module in a Rails project, and this post was very helpful in solving my problem! Many thanks!
Dave
Thanks! That helped.
Thanks, this relieved me from a lot of frustration :)
Post a Comment