Working With Design Patterns: Factory Method
Inheritance hierarchies containing an abstract base class present a small challenge for unit testing: How do common superclass methods get tested? Because you can't instantiate an abstract class, should you create an extension of it solely for purposes of testing? Or, do you re-test each of the public inherited methods for each subclass? Maybe it's sufficient simply to exercise the set of subclasses.
I can rule out the last choice: Public behavior needs to be explicitly tested, so it is not sufficient to test only against the subclass public methods. The first option, creating a test-only subclass of the target (the class being tested), is theoretically not a bad idea. It demonstrates that the public behavior works with stubbed implementations for abstract methods. In practice, though, it's probably best if the common public behavior works in conjunction with every derived class. No question then remains as to whether the public behavior works in context with the concrete class code.
That leaves you with the second choice—re-test each public method in each subclass. The downside is that this could generate a lot of redundant test code. Consider listing 1, which shows the test for a FloatColumn class. FloatColumn is an abstraction of an SQL column used for an object-to-relational mapping tool. Each such SQL column class requires a name upon construction, and must return that name when asked (via a getName method call). The common code in FloatColumn and other types like StringColumn and DateColumn can obviously be pushed into a base class, perhaps AbstractColumn. Listing 2 shows the relevant portions of both FloatColumn and AbstractColumn.
Listing 1: FloatColumnTest.
package persistence.types; import static org.junit.Assert.*; import org.junit.*; public class FloatColumnTest { protected Column column; static final String NAME = "name"; @Before public void setUp() { column = new FloatColumn("name"); } @Test public void create() { assertEquals("name", column.getName()); } @Test public void isEqualToColumnWithSameName() { assertTrue(column.equals(createColumn(NAME))); } @Test public void isUnequalToColumnWithDifferentName() { assertFalse(column.equals(createColumn(NAME + "x"))); } @Test public void isUnequalToNull() { assertFalse(column.equals(null)); } @Test public void isUnequalToObjectOfDifferentType() { assertFalse(column.equals("oranges")); } @Test public void isUnequalToObjectOfSubtype() { Column subInstance = new FloatColumn(NAME) { }; assertFalse(column.equals(subInstance)); } @Test public void hashIsEqualForEqualObjects() { assertEquals(column.hashCode(), createColumn(NAME).hashCode()); } private Column createColumn(String name) { return new FloatColumn(name); } // ... other specific FloatColumn tests ... }
Listing 2: The Column class implementations.
// AbstractColumn.java package persistence.types; abstract public class AbstractColumn implements Column { private String name; public AbstractColumn(String name) { this.name = name; } public String getName() { return name; } public boolean equals(Object object) { if (this == object) return true; if (object == null || !(object instanceof Column) || object.getClass() != this.getClass()) return false; Column that = (Column)object; return this.getName().equals(that.getName()); } public int hashCode() { return getName().hashCode(); } // ... } // FloatColumn.java package persistence.types; public class FloatColumn extends AbstractColumn { public FloatColumn(String name) { super(name); } // ... }
Page 1 of 3
This article was originally published on August 28, 2008