Friday, December 11, 2009

Is this a bad design?

Suppose we have this class.

public class Class1{
public int Foo(){
Class2 c2 = new Class2();
int x = c2.Bar();
return c2.Baz(x);
}
}


Seems simple enough, right? Now the question: how do you unit test this? Like, really unit test it. Assume that Bar and Baz hit a database or something. You want to write a test to _just_ make sure that that those methods are called. You can't really mock out Class2. Yet this meets even one of the strictest standards of coding, the Law of Demeter. So what's the deal?

2 comments:

Paul Barry said...

I assume what you are getting at is the difficulty of mocking Class2 since foo instantiates an object of that. In languages where mocking the instantiation is hard, in Java for example, that's why you need dependency injection. In Ruby, you don't have that problem. Here's an example:

Assuming the formatting doesn't come through, check it out here: http://gist.github.com/254456.

require 'test/unit'
require 'rubygems'
require 'mocha'

class Class1
def foo
c2 = Class2.new
x = c2.bar
c2.baz(x)
end
end

class Class2
def bar
21
end
def baz(x)
x * 2
end
end

class MyTest < Test::Unit::TestCase
def test_foo
n = 99
c2 = mock()
Class2.expects(:new).returns(c2)
c2.expects(:bar).returns(99)
c2.expects(:baz).with(99).returns(1)
Class1.new.foo
end
end

Paul Barry said...

Whoops, in that code example in the test I meant to use n rather than hard coding 99 in multiple places, but you get the drift :)