Tuesday, August 17, 2004

Two testing tales, one bad, one good

Tale one, package mismanagement

Here is an anti-pattern:

java/blarfage/Foo.java:

package blarfage; public class Foo { }

java/test/blarfage/FooTest.java:

package test.blarfage; import junit.framework.TestCase; public class FooTest extends TestCase { }

What is wrong here—where is the anti-pattern? Do not put test classes in a different package than that of the class under test. It looks clean and logical, but try it for a while and the pain of it hobbles your testing. Many useful tricks vanish, chief among them package-scope methods for tweaking the class under test. Don't just take my word for it: try it on any moderate-sized project and see for yourself.

Tale two, useful IoC trick

Here is a handy trick for configuring a class under test: provide a separate, protected constructor just for testing.

java/blarfage/Foo.java:

package blarfage; public class Foo { private final Bar favorite; public Bar(final Drink beer) { this(new Bar(beer)); } protected Bar(final Bar favorite) { this.favorite = favorite; } }

test/blarfage/FooTest.java:

package blarfage; import junit.framework.TestCase; public class FooTest extends TestCase { private TestFoo foo; protected void setUp() throws Exception { foo = new TestFoo(createMockBar()); } private static final TestFoo extends Foo { private TestFoo(final Bar favorite) { super(favorite); } } }

The idea is this: Say Bar is complex or expensive—java.sql.Connection is a good example. The public constructor takes the argument for constructing the private, expensive or complex object; the protected constructor takes the actual expensive or complex object so that a test can extend the class under test and use that protected constructor.

This trick is especially handy when you do not have control of code using the class under test (else you might refactor the public constructor to take the expensive or complex argument directly as the protected constructor does).

No comments: