Encapsulating Test Data and Expectations

I love it when I find new ways to improve my testing ability.  In this case, it’s not really new, just new to me.  I’m referring The Object Mother or Test Data Builder patterns used to encapsulate objects you need for testing.  I started playing with these patterns to simplify my tests and I found a way to further reduce the noise in my tests.

The basic concepts of these concept of these patterns is to put object populated with what you need behind simple factory methods to give you back the objects.  Test Builders can be more complicated, perhaps using a fluent interface to define the object exactly as you need for particular test.  The complexity for most of my tests fell somewhere between these too approaches.  The factory methods didn’t give me enough flexibility and most of my objects were not complex enough to warrant a full fluent interfaces.

Most of my tests seem to follow this pattern, where I define variables to hold my expected values, assign these values to the objects I need to test and then run my assertions against these expected values.

And then do my tests:

private string _expectedName = "name";
private string _expectedPhone = "phone";
private string _expectedPlus4 = "plus 4";
private string _expectedStateAbbrev = "state";
private string _expectedZip = "zip";
...
[Test]
public void should_populate_the_work_order_table_to_the_work_order_dto()
{
    
    WorkOrderMapper mapper = new WorkOrderMapper();
    DataSet workOrderDataSet = SetUpWorkOrderDataSet();
    var dto = mapper.MapFrom(workOrderDataSet);
    Assert.That(dto.CompanyName, Is.EqualTo(_expectedCompanyName));
    Assert.That(dto.Phone, Is.EqualTo(_expectedPhone));
    Assert.That(dto.StateAbbrev, Is.EqualTo(_expectedStateAbbrev));
    Assert.That(dto.ZipCode, Is.EqualTo(_expectedZip));
}

So I once I started exploring the Object Mother pattern, I realized that I could encapsulate not only the data I need for my objects, but also my expectation variables as well.  So I created TestData objects, where I declared the expected values and populated the objects with those values.  After a couple of different approaches, these test data classes started to look like this.

public class CustomerTestData
{
    public CustomerTestData()
        : this(null)
    {
    }
public ExpectationData Expectations { get;set;} 
public Customer Item{get;set;} 
    public class ExpectationData
    {
        public int Id = 0;
        public string CustNo = "CustNo";
        public string Company = "Company";
        public double Tax = 50;
        public double Discount = 10;
        public string GlLink = "gl";
        public bool IsTaxExempt = true;
        ...
        public ExpectationData()
        {
        }
   }
}

I could then use the Test Data Objects in my test like this:

[Test] 
public void should_sum_all_line_item_prices_times_quantity()
{
    var salesOrder = TestingData.GetSalesOrderTestData().SalesOrder;
    
    var actualTotal = salesOrder.GetOrderTotal();
    Assert.That(actualTotal, Is.EqualTo(50m));
    
}

Variability and Keeping Test Readable

There are two issues with this approach.  The first one you probably noticed right away.  There’s no way the same data can be used to satisfy all of your tests.  What if I have tests that need a null value or an invalid value?  Having the Expectations pre-defined reduce their usability to only positive tests.  The other issue is a little more subtle, but this approach reduces the readability of my test.  Now when someone wants to know what my tests are doing they have to look in two places, the test itself and the TestData objects.  The reduces their overall usefulness in describing the behavior of the system and it’s just plain annoying.

So what we need is the ability to define the values that are important to the test, but also create the objects in a valid state with default values unrelated to the test at hand.  We can do this with an Action<T> delegate.  Lets create the TestData with an Action<CustomerExpectations> delegate:

public CustomerTestData(Action<CustomerTestData.ExpectationData> overrideDefaultExpecations)
{
    Expectations = new CustomertTestData.ExpectationData();
    Item = Expectations.SetExpectionsOn(overrideDefaultExpecations);
}

Since delegates are first class citizens in .Net, we can pass that function around and execute when we need it.  Let’s create a method in ExpectationData class the creates our Customer object.  Now we’ll use the delegate to change the expected values.  This will change the value of the expectations before we set the values on the Customer object.

public Customer SetExpectionsOn(Action<CustomerTestData.ExpectationData> setExpectations)
{
    if (setExpectations != null)
    {
        setExpectations(this);
    }
    ArCust item = new ArCust();
    item.Id = Id;
    item.Company = Company;
    ...
    return item;

}

Now are our test looks like this:

[Test] public void should_sum_all_line_item_prices_times_quantity()
{
      var salesOrder = TestingData.GetSalesOrderTestData(e => {
                                                                                                  e.LineItem.Price = 10m;
                                                                                                  e.LineItem.Price = 5m;
                                                                                                 }).SalesOrder;
      var actualTotal = salesOrder.GetOrderTotal();
     Assert.That(actualTotal, Is.EqualTo(50m));
}

Now we can set the values for the parameters we are interested in, keeping our test objects in a valid state and maintaining the readability needed to understand the test without needing to refer to other object.

Conclusion

I’m still tweaking the syntax, but so far it has really cleaned up some of my tests.  However, it can fall apart if you have a complicated setup process.  Some of my tests require the same data in multiple objects, so that that they all  match when my service is executed.  Using the delegates to wire up 3 different objects in the setup became very difficult to read.  So I switched to the Builder pattern (and some refactoring) to clean up the process.  In other cases I needed a quick method to populate all the fields, so I wrapped it up in a factory method.  It is very easy to combine this approach with Builders and Factory Methods to give you as much control as you need over your test data.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

This entry was posted in TDD, Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://colinjack.blogspot.com Colin Jack

    Agree about dislike of approach in “should_sum_all_line_item_prices_times_quantity”, I prefer one of the approaches described here (http://xunitpatterns.com/Creation%20Method.html). In particular when calling an OBJECT MOTHER if something isn’t clear from the method name and isn’t passed in then I don’t think your tests should rely on it because if they do then they get fragile and confusing.

    Haven’t tried the solution you describe but did you consider just getting the OM to create an object and then changing any values that are important in the test? So in “should_sum_all_line_item_prices_times_quantity” you’d use OM to create an empty sales order (if that is allowed) and then you’d add the line items in the test (line items presumably being VALUE OBJECTS and thus suiting a builder). Does delegates add something over that approach.

    Anyway so far my approach has been to use TEST DATA BUILDER for value objects and then OBJECT MOTHER for Entities. However in a few cases I’ve added a BUILDER style fluent interface on top of the OBJECT MOTHER for an entity, for example this fluent interface gives me an easy way to say “Give me a customer with a relationship to an account manager who works for the company Spondooliks”. Gotta finish a blog entry about the approach I’ve used as I’m interested in knowing why its total rubbish :)

    Note: I capitilized pattern names to try and make it clear what I mean.

  • jcteague

    @Colin
    Nice Post. One of problems I ran into quickly was the number of factory methods I needed with Object Mother pattern, so I wanted a way to set specific values in one place and keep the number down. The delegate lets me set whatever values I want without an explosion of factory methods and overloads.

    If I was using a lot of the fields in my object (mapping from DTO’s as an example) and I set the values in question after the OM created the object, I would be in the same situation I was trying to avoid.

  • http://aaron.codebetter.com Aaron Jensen
  • jcteague

    @Aaron
    Yes, I read that a long time ago. Your approach is definitely one of the more elegant approaches to fluent builders out there. Like I said in the post and my comment to Colin, for complicated objects fluent builder are great. But if you have simple object and you don’t want a bunch of one-off static methods, this approach is useful