Avg. Rating 3.5

Problem

How to easily share simple and complex test data instances between multiple TestCases?

Solution

Create a factory class that can generate required test data instances.

Detailed explanation

This recipe extends the Create a FlexUnit TestCase recipe.

A common unit testing need is to have multiple different TestCases share the same or similar test data. This data maybe simple like an object which represents an Address or it maybe a complex object such as an Order which has many interrelated entities that must be setup in a particular manner. Instead of cutting and pasting code or trying to load data from an external resource to create and initialize such objects in each TestCase, the creation can be centralized into a factory. This type of test data centralization is referred to as the ObjectMother pattern.

In its simplest form the ObjectMother is a single utility class with a static method for creating each type of object that is needed. Typically the creation methods will come in two forms. The first form requires passing in values for each property that needs to be set and the method just assembles the object. The second form requires few or no arguments and the method provides realistic intelligent defaults for each field. As additional object types are needed they can use the lower level creation methods to build more and more complex objects.

Another benefit of the ObjectMother class is to provide testing constants or other magic values that many tests will reference.

Below is an example of what a simple ObjectMother implementation could look like:
package
{
public class ObjectMother
{
public static const SHIPPING_ZIP_CODE:String = "0123";

public static function createAddress(line:String, city:String, state:String, zip:String):Address
{
var address:Address = new Address();
address.line = line;
address.city = city;
address.state = state;
address.zip = zip;
return address;
}

public static function createAddressShipping():Address
{
return createAddress("123 A Street", "Boston", "MA", SHIPPING_ZIP_CODE);
}

public static function createAddressBilling():Address
{
return createAddress("321 B Street", "Cambridge", "MA", "02138");
}

public static function createOrder(lineItems:Array = null):Order
{
var order:Order = new Order();
order.shippingAddress = createAddressShipping();
order.billingAddress = createAddressBilling();
for each (var lineItem:LineItem in lineItems)
{
addLineItemToOrder(order, lineItem);
}
return order;
}

public static function addLineItemToOrder(order:Order, lineItem:LineItem):void
{
order.addLineItem(lineItem);
}
}
}

Starting with a simple Address object, a standardized parameter creation method createAddress() is defined. Two helper functions createAddressShipping() and createAddressBilling() are added to provide a quick way for TestCase methods to get access to fully fleshed out Address instances. The helper functions build on the generic createAddress() function utilizing any creation logic already written in that method. This tiered creation policy becomes handy as the types of objects being created become more complex, as shown in the createOrder() example, or there are many different steps to creating a single object.

Since new instances of objects are created each time a method is called, changes made by one TestCase won't have unintended side effects on other TestCases. At the same time since the test data objects are centralized changing the data in the ObjectMother to support a new test, may break existing brittle tests. This is usually a minor concern compared to the benefit of having readily accessible test data.
Report abuse

Related recipes