Advantages to using an ObjectMother and Why should I test string = string.Empty?

In my never ending quest for the holy grail I continue to ask more questions than I answer. Recently as I was putting together our unit tests for a project, I decided to give the ObjectMother pattern a whirl. This is a pattern dreamed up by some ThoughtWorkers and is a simple, scalable framework whose sole purpose is to construct and facilitate the customization of test objects. ObjectMother manages the lifecycle of test objects including creation, customization and, when necessary, deletion. I think it's an interesting pattern but is there a real advantage to using it over just creating and initializing objects in your test [Setup].

Lets say your have a Task class in your Domain that manages tasks for users. Here's our Task class:

public class Task

{

      private string name = string.Empty;

      private string owner = string.Empty;

      private DateTime dueDate = DateTime.Now.AddDays(7);

      private DateTime notificationDate = DateTime.Now;

 

      public Task()

      {

      }

 

      public string Name

      {

            get { return name; }

            set { name = value; }

      }

 

      public string Owner

      {

            get { return owner; }

            set { owner = value; }

      }

 

      public DateTime DueDate

      {

            get { return dueDate; }

            set { dueDate = value; }

      }

 

      public DateTime NotificationDate

      {

            get { return notificationDate; }

            set { notificationDate = value; }

      }

}

There are some requirements the business tells us that we want to test against.

  • The due date must be greater than the notification date
  • A task must be assigned to someone
  • A task must have a name

This leads us to building some tests that look something like this:

[TestFixture]

public class TaskTests

{

      [SetUp]

      public void SetUp()

      {

      }

 

      [TearDown]

      public void TearDown()

      {

      }

 

      [Test]

      public void TaskCreation()

      {

            Task task = new Task();

            task.Name = "Release Windows under the GPL";

            task.Owner = "Bill Gates";

            task.DueDate = DateTime.Parse("12/31/2009");

            task.NotificationDate = DateTime.Parse("01/01/2010");

            Assert.AreEqual(task.Name, string.Empty);

            Assert.AreEqual(task.Owner, string.Empty);

            Assert.IsNull(task.NotificationDate);

            Assert.IsNull(task.DueDate);

      }

 

      [Test]

      public void DueDateAfterNotificationDate()

      {

            Task task = new Task();

            task.DueDate = DateTime.Now.AddDays(1);

            task.NotificationDate = DateTime.Now;

            Assert.IsTrue(task.DueDate > task.NotificationDate, "Due date must be after notification date.");              

      }

 

      [Test]

      public void DueDateBeforeNotificationDate()

      {

            Task task = new Task();

            task.DueDate = DateTime.Now.AddDays(-1);

            task.NotificationDate = DateTime.Now;

            Assert.IsTrue(task.DueDate < task.NotificationDate, "Due date cannot be before notification date.");              

      }

}

Using the ObjectMother pattern, I can initialize my task object with the appropriate values using static creators like so:

public class ObjectMother

{

      public ObjectMother()

      {

      }

 

      public static Task CreateTask()

      {

            Task task = new Task();

 

            task.Name = "Release Windows under the GPL";

            task.Owner = "Bill Gates";

            task.DueDate = DateTime.Parse("12/31/2009");

            task.NotificationDate = DateTime.Parse("01/01/2010");

 

            return task;

      }

}

And I modify my tests to check against values from the ObjectMother like so:

[Test]

public void TaskCreation()

{

      Task task = ObjectMother.CreateTask();

      Assert.AreEqual(task.Name, string.Empty);

      Assert.AreEqual(task.Owner, string.Empty);

      Assert.IsNull(task.NotificationDate);

      Assert.IsNull(task.DueDate);

}

The traditional way of initializing my task object would be in the [Setup] method in my test class (using a private field called task of type Task) and initializing the values to some constant in my test. Alternately to the ObjectMother method above, I could create static fields in the ObjectMother the same way for comparision (or pass in values from the Test to the ObjectMother.CreateTask method to initialize them to known values). Here's the setup for my test in my [TestFixture]:

private Task task;

private string defaultTaskName = "Release Windows under the GPL";

private string defaultTaskOwner = "Bill Gates";

private DateTime defaultTaskDueDate = DateTime.Parse("01/01/2010");

private DateTime defaultTaskNotificationDate = DateTime.Parse("12/31/2009");

 

[SetUp]

public void SetUp()

{

      task = new Task();

      task.Name = defaultTaskName;

      task.Owner = defaultTaskOwner;

      task.DueDate = defaultTaskDueDate;

      task.NotificationDate = defaultTaskNotificationDate;

}

And my test would compare values to private fields of the test class which makes the creation test look something like this:

[Test]

public void TaskCreation()

{

      Assert.AreEqual(task.Name, defaultTaskName);

      Assert.AreEqual(task.Owner, defaultTaskOwner);

      Assert.IsNull(task.NotificationDate);

      Assert.IsNull(task.DueDate);

}

So where is the advantage to using an ObjectMother? Rather than comparing values against the private fields in my TestFixture I could compare them against static fields in the ObjectMother class. Maybe the ObjectMother is for more complicated objects that need lifecycle management but then do I really want to mix object creation in my tests?

More importantly, should I really compare a DateTime value to a DateTime value? Is that a valid test or am I testing the assignment of variables in the .NET framework. True, if you put logic into the Task class setter for DueDate and NotificationDate you can throw an exception so it doesn't allow invalid dates which would give me a more valid test. How many times have you seen this test:

[Test]

public void TestValidOwner()

{

      task.Owner = "John Smith";

      Assert.AreEqual(task.Owner,  "John Smith", "Task Owner does not match");

}

I've just seen too many samples claiming that it's a valid test when all they're doing is comparing a string to a string (alternately getting the string from a const value in the test class instead). How much value does this test add to your system?

7 Comments

  • I find a Mother helpful when the domain starts to get more complex. Sometimes you need to test the interaction between several objects, and sometimes mocks aren't appropriate.



    I think, as you've realised, your example test is far too simplistic to show the value of an ObjectMother.



    Jim

  • The expected value is the first argument to Assert.AreEqual(). Not following that makes your code much harder to understand for a human reader, and you will get reversed (misleading) messages when a test fails ( &quot;got 5 expected 8&quot; isto &quot;got 8 expected 5&quot; ).

  • The downside to using this type of pattern is one you should look at as well - you are now also testing your object *And* the ObjectMother.



    While having an object creator / lifecycle manager is handy in many cases (indespensible sometimes, sure), you do need to realize that you're now making your tests test code that will only be run during testing, not production!

  • &quot;you are now also testing your object *And* the ObjectMother&quot;



    But look at it another way: an ObjectMother formalises and isolates code that would otherwise be duplicated across multiple tests. Any test code can potentially have bugs in it, but at least a whole class of bugs are now confined to one place.



    Jim

  • Phillip and Jim raise an interesting point on the fact that the ObjectMother is now invoking the Domain objects and even that creation (in the ObjectMother) could cause a failure. What happens if the ObjectMother creation throws an exception. As Jim pointed out, it's now isolated but it does seem like you might get into a vicious circle.

  • Besides being too simple, this isn't the right example for an ObjectMother because you would not be creating an instance of the class being tested with an ObjectMother.



    When you have some sort of object hierarchy or a complicated object that takes a significant amount of code to set up, AND interacts with several other classes, an ObjectMother is advantageous because you can remove duplication from the [TestFixture] classes and make your unit tests more clear. I also like to use the pattern to create randomized data for testing things like filtering and mapping to UI code.



    As far as testing the ObjectMother at the same time, it's not so much testing as it is that you're making an assumption about the state of the object returned from the ObjectMother. One thing I like to do is to put some Assert.AreEqual() calls on the object returned just to validate and kinda document the assumption.



    I'm going to blog on ObjectMother soon with an example from my open source project.

  • Thanks for the info Jeremy. Yeah, the object is too simple and wasn't right for using the pattern. I took a look at StructureMap which is quite nice and has a lot of nice patterns and examples.

Comments have been disabled for this content.