minitest and mocha, mocks and stubs

Debugger, an open-source project I’m working on has both Minitest and Mocha in its gemspec. For the last few months, I tried to only use Minitest until today, when I hit a limitation.

My goal was to test one method on RdebugOptionParser, a class I extracted. The method had to require a file passed in as an argument. To make my test a true unit test–that is, one that asserts on only the behavior I’m implementing–I had to go beyond Minitest. The file I pass in as an argument shouldn’t actually be required, I only want to test that Kernel#require was called.

Minitest makes it easy to mock out an entire object. If you want to stub out a single method on an object, that should be easy, but I couldn’t get it working:

# Excerpted from Minitest docs def test_stale_eh obj_under_test = refute obj_under_test.stale? Time.stub :now, do # stub goes away once the block is done assert obj_under_test.stale? end end

Even if I could get stubbing to work, I could only stub require but not assert that it was called. As I learned today, mocks are testing tools that allow you to fake and set expectations on objects that you’re not testing directly. On the other hand, stubs allow you to fake objects you aren’t directly testing.

So it was clear I wanted a mock. The only problem is that with Minitest, I could only mock the entire RdebugOptionParser object. Mocking RdebugOptionParser would mean that I’d mock out the method I was actually testing too.

After an hour of searching for how to use Minitest to mock out one method on an object while keeping the rest of the object intact, I resigned to looking at options provided by other gems. Mocha’s syntax looked pretty clean, so I looked into adding it to the gemspec. Lo and behold, it was already there, huzzah!

The syntax is quite simple:

RdebugOptionParser.instance.expects(:require).with('foo').returns(true) # Then call the method being tested call_rdebug_parse_with_arguments ["--require=foo"]