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:
1
2
3
4
5
6
7
8
9
10
# Excerpted from Minitest docs
def test_stale_eh
obj_under_test = Something.new
refute obj_under_test.stale?
Time.stub :now, Time.at(0) 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:
1
2
3
RdebugOptionParser.instance.expects(:require).with('foo').returns(true)
# Then call the method being tested
call_rdebug_parse_with_arguments ["--require=foo"]