First, let me make it clear that I really like BDD, I really like mocks, and I really like dynamic languages. RSpec does a pretty good job of combining all three.
However, there's one disadvantage that duck-typed languages suffer when it comes to using mocks to drive the design of the interfaces between objects.
The following example is lifted from the excellent paper Mock Roles, not Objects, by Steve Freeman, Nat Pryce, Tim Mackinnon and Joe Walnes. If you haven't already read it, it's well worth it.
Here's a test case:
-
public class TimedCacheTest {
-
-
public void testLoadsObjectThatIsNotCached() {
-
ObjectLoader mockLoader = mock(ObjectLoader.class);
-
TimedCache cache = new TimedCache(mockLoader);
-
-
mockLoader.expect(once()).method("load").with( eq(KEY) )
-
.will(returnValue(VALUE));
-
mockLoader.expect(once()).method("load").with( eq(KEY2) )
-
.will(returnValue(VALUE2));
-
-
assertSame( "should be first object", VALUE, cache.lookup(KEY) );
-
assertSame( "should be second object", VALUE2, cache.lookup(KEY2) );
-
-
mockLoader.verify();
-
}
-
}
And here's some code to pass the test:
In order to get this to compile1, you also need to introduce an interface:
When you've finished specifying the behaviour of TimedCache, you simply move on to an implementation of ObjectLoader, and repeat the process.
Now here's something similar in Ruby. First the test:
-
KEY = 1
-
KEY2 = 2
-
VALUE = "one"
-
VALUE2 = "two"
-
-
describe "An object that is not cached" do
-
before :each do
-
@mock_loader = mock "loader"
-
@cache = TimedCache.new @mock_loader
-
end
-
-
it "should be loaded" do
-
@mock_loader.should_receive(:load).with(KEY).and_return VALUE
-
@mock_loader.should_receive(:load).with(KEY2).and_return VALUE2
-
@cache.lookup(KEY).should == VALUE
-
@cache.lookup(KEY2).should == VALUE2
-
end
-
end
And the code to make it pass:
-
class TimedCache
-
def initialize loader
-
@loader = loader
-
end
-
-
def lookup key
-
@loader.load key
-
end
-
end
The difference here is that Ruby doesn't have interfaces. The mock object loader is just an object of no particular class, with a bunch of methods on it (OK, just the one in this case), which you then have to remember to include in the real implementation.
I wonder whether it would be useful to be able to print out a list of the methods that you've mocked, to give you a starting point when implementing the classes you've mocked. Here's a very quick-and-dirty hack to Spec::Mocks::Proxy that prints a list to the console while the test's running:
-
module Spec
-
module Mocks
-
class Proxy
-
...
-
def verify_expectations
-
# HACK STARTS HERE!
-
puts " mocked on #{@name}:"
-
@expectations.each do |expectation|
-
puts " #{expectation.sym.to_s}(#{expectation.expected_args.join ', '})"
-
end
-
#HACK ENDS HERE
-
@expectations.each do |expectation|
-
expectation.verify_messages_received
-
end
-
end
-
...
Here's the output:
An object that is not cached
mocked on loader:
load(1)
load(2)
- should be loaded
Really this probably belongs in a custom formatter, but it doesn't look like the right hooks currently exist to implement it that way.
Technorati Tags: bdd, mocks, ruby, rspec