Wednesday, September 10, 2008

Stubbing/Mocking constants with Mocha

As far as google told me, it seems you can't use Mocha against constants.
One alternative proposed is changing the constant in the test where you need it like these:

class Module
def redefine_const(name, value)
__send__(:remove_const, name) if const_defined?(name)
const_set(name, value)
end
end
And inside the test case you change the constant to what you need and restore it after the test ends:

Object.redefine_const(:RAILS_ENV, 'production')
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:

class A
def rails_env
RAILS_ENV
end
#...some more code that gets the constant RAILS_ENV using the method rails_env...
end
And then in your test you just do a normal expectation:

A.any_instance.expects(:rails_env).returns 'production'
You could even have some module that wraps all useful constants in your app, so constant dependent code gets easily testable and clean.

5 comments:

Ken Collins said...

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

oboxodo said...

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.

Dave Hollingworth said...

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

Sharad Jain said...

Thanks! That helped.

Anonymous said...

Thanks, this relieved me from a lot of frustration :)