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?