Thursday, January 29, 2009

Test Utilities using extension methods

So, I was working with Jeff McWherter on some TDD stuff in C# today, and I came upon a trick that I really like. When I used to do C#, I was working in the 2.0 version of the .net framework. This meant that, while I had cool things like generics and anonymous methods to help me out, I didn't have extension methods. Well, Jeff and I were working in .net 3.0, so I had the benefits.

We were writing some tests that need a couple instances of a data object. Well, my first test new-ed up the instance and added it to the collection. This instance needed to have the due date property set, so I did something like this:

List<MyThing> things = new List<MyThing> {
new MyThing { DueDate = DateTime.Now.AddDays(1),
new MyThing { DueDate = DateTime.Now.AddDays(-14)}}
};
view raw gistfile1.cs hosted with ❤ by GitHub


Well, there's a bit of annoying duplication there, so I changed it to:

List<MyThing> things = new List<MyThing> {
thing(DateTime.Now.AddDays(1)),
thing(DateTime.Now.AddDays(-14))
}
view raw gistfile1.cs hosted with ❤ by GitHub


with a method later on that looks like this

public MyThing thing(DateTime duedate){
return new MyThing {DueDate = duedate};
}
view raw gistfile1.cs hosted with ❤ by GitHub


Okay, a bit better.

A little bit later, we had another test fixture that used the data object in a similar way, so I was looking for a way to bring the thing() method into a scope that was available for the other fixture, as well. When I was doing 2.0, I would have probably built a base class for my test fixtures and subclasses it in both fixtures. Yuk! Those who know me know that I have a special, black, cold place in my heart for subclassing like this.

Enter extension methods


I want a way to have some utility methods that I can use whenever I have a fixture that needs to build things. Aha! Let's make an interface:

public interface TestsThings {}

And, add an extension method to it:

public static class ThingTestingUtilities {
public static Thing NewThing(this TestsThings tester, DateTime duedate) {
return new MyThing { DueDate = duedate};
}
}
view raw gistfile1.cs hosted with ❤ by GitHub


Cool. Now, my test fixtures can implement TestsThings and get the method, so the construction of the lists will now look like this:

List<MyThing> things = new List<MyThing> {
this.NewThing(DateTime.Now.AddDays(1)),
this.NewThing(DateTime.Now.AddDays(-14))
}
view raw gistfile1.cs hosted with ❤ by GitHub


Unnecessary Construction Parameters? Yuk!



Well, not happy to leave a bad thing bad, I decided to take it one step further. I want to be able to write

List<MyThing> things = new List<MyThing> {
this.NewThing().WithDueDate(DateTime.Now.AddDays(1)),
this.NewThing().WithDueDate(DateTime.Now.AddDays(-14))
}
view raw gistfile1.cs hosted with ❤ by GitHub


How to do that? Hmm.... Ah, here we go. Let's change ThingTestingUtilities to look like this:

public static class ThingTestingUtilities {
public static Thing NewThing(this TestsThings tester) {
return new MyThing;
}
public static Thing WithDueDate(this MyThing thing, DateTime duedate) {
thing.DueDate = duedate;
return thing;
}
}
view raw gistfile1.cs hosted with ❤ by GitHub


Works much better. But, there is still a matter of duplication in the DateTime.Now... stuff. So, let's take this a step further. What if I could write this:

List<MyThing> things = new List<MyThing> {
this.NewThing().WithAnOffsetDueDateInDays(1),
this.NewThing().WithAnOffsetDueDateInDays(-14)
}
view raw gistfile1.cs hosted with ❤ by GitHub

That looks much better. Yeah, if I was in a different language, I'd do something like:
this.NewThings().WithAnOffsetDueDate(1.days);
[Update: Steven Harman alerted me that you can do this. I'm too lazy right now to implement it, though. Maybe later]
[Update2: Leon Gersin added a comment showing how to extend it like 1.days. Check it out.]

So, let's add a helper method. The utility class looks like this now:

public static class ThingTestingUtilities {
public static Thing NewThing(this TestsThings tester) {
return new MyThing;
}
public static Thing WithDueDate(this MyThing thing, DateTime duedate) {
thing.DueDate = duedate;
return thing;
}
public static Thing WithAnOffsetDueDateInDays(this MyThing thing, int days){
return thing.WithDueDate(DateTime.Now.AddDays(days));
}
}
view raw gistfile1.cs hosted with ❤ by GitHub


Cool, compiles, runs, now my construction looks like this:

List<MyThing> things = new List<MyThing> {
this.NewThing().WithAnOffsetDueDateInDays(1),
this.NewThing().WithAnOffsetDueDateInDays(-14)
}
view raw gistfile1.cs hosted with ❤ by GitHub


Extension methods can definitely help do some neat stuff. I don't have to subclass, I can add methods by simply implementing an interface. This also gives me the ability to create interfaces that are focused on different tasks that my test fixtures might need, so I don't have to load up subclasses with unnecessary mixes of unrelated methods.