http://www.developer.com/

Back to article

Working With Design Patterns: Factory Method


August 28, 2008

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);
   }
   // ...
}

AbstractColumn not only contains common code for construction and getName, but also for equals and hashCode methods, both of which key upon column name. Based on how I use these column classes in my application, all other column types can take advantage of this simple definition for equality.

The tests, however, are all going to look similar to FloatColumnTest, with seven test methods looking exactly the same. Other than tests for the specific behavior of FloatColumn (which I've not specified yet), the only thing that differs is the @Before method, which will have to construct a column of the appropriate type for each other test class (StringColumnTest, DateColumnTest, and so on).

There are at least a few ways to eliminate this redundancy. The preferred solution solution is simple: Push all of the common test code into an abstract test class. Define an abstract method in the abstract test class called createColumn, and have the @Before method call this abstract method. Implement the createColumn method in each subclass. Voilà! You have just used the design pattern known as factory method. The createColumn is the "factory method"—a method whose sole job is to construct objects of appropriate types. The benefit of a factory method is that it allows code in an abstract class defer object creation to subclasses.

As each concrete test class executes, it executes the common base class methods defined in AbstractColumnTest, but using an appropriate subclass type. Thus, each test method defined in the abstract test case gets executed once for each subclass. This may seem wasteful from an execution standpoint, but I'd rather have the confidence that the methods work in all "real" contexts.

Listing 3 shows the simplified FloatColumnTest class after applying the factory method pattern. All that remains of the otherwise rampant common test code is an implementation of the createColumn method. Listing 4 shows how I've moved all of the tests (previously defined in FloatColumnTest) up into AbstractColumnTest.

Listing 3: A simplified FloatColumnTest.

package persistence.types;

import static org.junit.Assert.*;
import org.junit.*;

public class FloatColumnTest extends AbstractColumnTest {
   @Override
   protected Column createColumn(String name) {
      return new FloatColumn(name);
   }

   // ... other specific FloatColumn tests ...
}

Listing 4: AbstractColumnTest.

package persistence.types;

import org.junit.*;
import static org.junit.Assert.*;

abstract public class AbstractColumnTest {
   protected static final String NAME = "name";
   protected Column column;

   @Before
   public void setUp() {
      column = createColumn(NAME);
   }

   abstract protected Column createColumn(String 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 AbstractColumn(NAME) {
         public String declaration() {
            return null;
         }

         public String sqlValue(Object object) {
            return null;
         }
      };
      assertFalse(column.equals(subInstance));
   }

   @Test
   public void hashIsEqualForEqualObjects() {
      assertEquals(column.hashCode(),
                   createColumn(NAME).hashCode());
   }

   @Test
   public void hash() {
      Column duplicateColumn = createColumn(NAME);
      assertEquals(column.hashCode(),
                   duplicateColumn.hashCode());
   }
}
Note: If you use the abstract test case pattern, you'll need to alter your Ant build scripts to ignore the abstract test classes. Otherwise, Ant will attempt to instantiate and execute the tests in the abstract test class. Typical shops follow a consistent naming convention, which makes for a simple Ant solution:

   <exclude name="**/Abstract*Test.java" />

Eclipse and other IDEs are smart enough to ignore these classes by default.

The factory method is a simple inheritance-based design pattern. Most programmers would implement the pattern sooner or later, even if they didn't know what a factory method was. The value of the pattern in this case is that the construct now has a standard name, and that the pattern recommends a preferred solution instead of at least a couple other variant implementations. This implementation of the factory method pattern—in the context of creating a hierarchy of unit tests—is relevant enough to a fairly common test problem that it has its own pattern name: abstract test case.



Click here for a larger image.

Figure 1: The factory method pattern.

About the Author

Jeff Langr is a veteran software developer with over a quarter century of professional software development experience. He's written two books, including Agile Java: Crafting Code With Test-Driven Development (Prentice Hall) in 2005. Jeff contributed to Uncle Bob Martin's new book, Clean Code (Prentice Hall, August 2008). Jeff has written over 80 articles on software development, with over thirty-five appearing at Developer.com. You can find out more about Jeff at his site, http://langrsoft.com, or you can contact him via email at jeff at langrsoft dot com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date