TestDataBuilder in C# 4

Creating data for unit tests can get cumbersome. There exists some patterns to speed up the process, like Object Mother and Test Data Builder.

I particularly like the Test Data Builder pattern, which I am currently using in my day to day work.

Existing pattern

Here is a pretty standard version of it:

public class BlogPostDataBuilder
{
    private DateTime publishDate;
    private Blogger blogger;

    public BlogPostDataBuilder()
    {
        blogger = new BloggerDataBuilder().Build(); 
        publishDate = DateTime.Now;
    }
                
    public BlogPost Build()
    {
        var blogPost = new BlogPost(blogger, publishDate);
        blogPost.AddTag("test tag");

        return blogPost;
    }

    public BlogPostDataBuilder WithPublishDate(DateTime publishDate)
    {
        this.publishDate = publishDate;
        return this;
    }

    public BlogPostDataBuilder WithBlogger(Blogger blogger)
    {
         this.blogger = blogger;
         return this;
    }
}

In the constructor, you initialize the data members to a standard value that can be used for most tests. Then, you can use your Test Data Builder in the following fashion:

var testBlogPost = new BlogPostDataBuilder().Build();

This simplifies the act of supplying your test objects with dummy data when you do not care about the value of said data. When you do care about the data, for example you have a test that needs to verify that X happens when the publishDate is in the future, you can only specify the values which are important for the test at hand.

var futureDate = DateTime.Now.AddDays(1);

var testBlogPost = new BlogPostDataBuilder()
                       .WithPublishDate(futureDate)
                       .Build();

This pattern makes it very clear which information is important in your test by making it very salient, while abstracting the non essential information.

Notice that the With methods return a Test Data Builder, so you can chain as many With method as you like.

A word of advice, when you write a new Test Data Builder, there is no need to write With methods for those properties that you don’t need to override right now. This will lessen the initial time investment of writing the Test Data Builder. Beside, as they say, YAGNI (you ain’t gonna need it). It is always safer the wait until you need something right now before starting to write it.

If you need to write a new test in the future which needs a specific property, like setting a specific post category, you create a new With method. Since none of your old tests are using this new With method, they will not be affected.

public BlogPost Build()
{
    var blogPost = new BlogPost(blogger, publishDate);
    blogPost.AddTag("test tag");

    if (category != null)
        blogPost.AddCategory(category);

    return blogPost;
}

public BlogPostDataBuilder WithCategory(Category category)
// ...

Using Test Data Builder will not only speed up writing subsequent tests. It will make the tests clearer. Finally it will also reduce the impact of a change in how you create your objects, which will lessen the pain of refactoring your tests.

Enter C# 4

So this was a pretty standard implementation of the pattern. Some of the new features of C# 4, namely named arguments and default parameters, got me thinking of a new way to implement this.

Here it is:

public class BlogPostDataBuilder
{
    private Blogger blogger;
    private Category category;

    public BlogPostDataBuilder(Blogger blogger = null, Category category = null)
    {
        this.blogger = blogger;
        this.category = category;
    }
                
    public BlogPost Build()
    {
        if (blogger == null)
            blogger = new BloggerDataBuilder().Build();

        var blogPost = new BlogPost(blogger, DateTime.Now);
           
        if (category != null)
            blogPost.AddCategory(category);

        return blogPost;
    }
}

And you would use it likewise:

var testBlogger = new Blogger();
var testCategory = new Category();

var testBlogPost = new BlogPostDataBuilder(blogger: testBlogger,
                                           category: testCategory)
                       .Build();

On the plus side, there is no need to code the With methods, and the resulting class is much leaner. Using the builder is also less verbose.

On the negative side, you run into a problem with DateTime parameters since you cannot use a DateTime as a constant for the default value of a default parameter. There are two ways to get around this.

The first is to provide a WithPublishDate method. This mix the two approach but it works. The other would be to make PublishDate a Nullable DateTime in your builder class.

Conclusion

If you have any opinions regarding the traditional Test Data Builder versus the C# 4 one, please leave a comment!

One thought on “TestDataBuilder in C# 4

  1. Nice post,
    at first I was skeptical about test data builder, but after a really short time. It surely becomes a natural thing to do.

Leave a comment