Best way to test Ruby class methods? by jgn on Wednesday, September 7, 2011 in Ruby and Code

Let's say that we have a Ruby object with some class methods. In my case, we want to be able to inject a driver at the class level that all instances will use:

[code language='ruby'] class Mod::Thingy def self.driver end def self.driver=(d) end end

And let's say that .driver has a default value. So the class looks more like this:

[code language='ruby'] class Mod::Thingy def self.driver @driver || DefaultDriver end def self.driver=(d) @driver = d end end

Now here's the dealio. Let's say that we test for the default value first. So we check Mod::Thingy.driver == DefaultDriver. Then we test the setter. We do Mod::Thingy.driver = OtherDriver, and then check the return value of Mod::Thingy.driver. Fair enough.

But what if we ran the examples in reverse? Now we do Mod::Thingy.driver = OtherDriver first, check it; but now in our second test, we can't test the default value, because the state of Mod::Thingy has been changed.

Strategies:

  1. We might use RSpec's around feature to save the state of Mod::Thingy.driver and restore it afterwards. What I don't like about this is that now the around method has to know all about all of the setters at the class level. Meanwhile, it is possible to imagine objects that are difficult to set back to their default state: Perhaps the class methods have order dependencies themselves. This would make the test need to know a lot about the inner state of the object.
  2. We might provide a .reset! method on the object. But this seems a bit nutty: Add a method just to make testing easier?
  3. What about this in an RSpec before(:each) block:

[code language='ruby'] Mod.send(:remove_const, 'Thingy') load "#{PROJECT_ROOT}/lib/mod/thingy.rb"

Now we're getting a nice clean class. To me, (3) is more parallel to the testing convention of new'ing up an object instance for the test.

Thoughts?

comments powered by Disqus