Mark Needham

Thoughts on Software Development

C# Test Builder Pattern: My current thinking

with 4 comments

I’ve written previously about the test builder pattern in C# and having noticed some different implementations of this pattern I thought it’d be interesting to post my current thinking on how to use it.

One thing I’ve noticed is that we often end up just creating methods which effectively act as setters rather than easing the construction of an object.

This seems to happen most commonly when the value we want to set is a boolean value. The following is quite typical:

public CarBuilder WithSomething(boolean value)
{
	this.something = value;
	return this;
}

The usage would be like so:

new CarBuilder().WithSomething(true).Build();

It doesn’t read too badly but it seems to be unnecessary typing for the user of the API. If we’re going to use the fluent interface approach then I would prefer to have two methods, defined like so:

public CarBuilder WithSomething()
{
	this.something = true;
	return this;
}
 
public CarBuilder WithoutSomething()
{
	this.something = false;
	return this;
}

We could then use this like so:

new CarBuilder().WithSomething().Build();
...
new CarBuilder().WithoutSomething().Build();

That requires more code but I think it’s a bit more expressive and makes life easier for the user of the API.

An alternative approach which my colleague Lu Ning showed me and which I think is actually better is to make use of the object initializer syntax if all we have are setter methods on a builder.

We might therefore end up with something like this:

public class FooBuilder 
{
	public string Something = "DefaultSomething";
	public boolean SomethingElse = false;
 
	public Foo Build()
	{
		return new Foo(Something, SomethingElse);
	}
}
new FooBuilder { SomethingElse = true }.Build();

With this approach we end up writing less code and although we use public fields on the builder I don’t think it’s a big deal since it allows us to achieve our goal more quickly. If we need other methods that take out the complexity of construction then we can easily just add those as well.

Another thing to note when using this pattern is that we don’t need to override all the object’s attributes on every single test. We only need to override those ones which we are using in our test. The rest of the values can just be defaulted.

[Test]
public void ShouldDoSomething()
{
	var foo = new FooBuilder { Bar = "myBar", Baz = "myBaz", SomethingElse = "mySE", AndAgain = "..." }.Build();
 
 
	// and then further on we only check the value of Bar for example
 
	Assert.That(someValue, Is.EqualTo("myBar"); 
}

We don’t need to specifically set any of the values except ‘Bar’ because they are irrelevant and create a clutter in the test which means it takes longer to understand what’s going on.

This would be preferable:

[Test]
public void ShouldDoSomething()
{
	var foo = new FooBuilder { Bar = "myBar" }.Build();
 
 
	// and then further on we only check the value of Bar for example
 
	Assert.That(someValue, Is.EqualTo("myBar"); 
}

Something which I’ve been wondering about recently is understanding the best way to describe the case where we don’t want to define a specific value i.e. we want it as null.

I’d normally just set it to null like so:

new FooBuilder { Bar = null }.Build();

But we could make the API have a specific method and I haven’t really decided whether there’s much value to doing so yet:

new FooBuilder().WithNoBar().Build();

Written by Mark Needham

January 13th, 2010 at 1:37 am

Posted in Coding

Tagged with ,

  • http://andypalmer.com Andy Palmer

    I like the WithoutSomething as opposed to WithSomething(false), although I think that specifying the absence of something is a little unusual (and suggests an underlying implicit assumption)

    I’m not sure that the object initializer syntax makes it more readable.. would have to play with that a bit more.

    “[...] the best way to describe the case where we don’t want to define a specific value i.e. we want it as null”

    Do you _really_ want a null when what you appear to want is a value that doesn’t matter?

    I think null is a pretty specific value, otherwise, why would people bother with all these “if (null == bar)” checks.

  • http://zdsbs.blogspot.com Zach

    I really like they way it looks like named parameters. But without those fields final you do loose the immutability. Which shouldn’t be a huge deal if you keep your tests small, but there are certainly time I do like being able to say things like:

    fooBuilder = new FooBuilder().with(bar).with(baz);
    f1 = fooBuilder.build();
    f2 = fooBuilder.with(bar2).build();

    I do like new FooBuilder() { Bar = “myBar”};
    but it’s not too unreadable when you write your builder FooBuilder().with(myBar) vs FooBuilder().withBar(myBar)

    Anyway, it’s always about trade offs :P

  • http://intwoplacesatonce.com DCam

    @Mark: The Rainsberger post about “what your tests don’t need to know will hurt you” seems to suggest a method like .ArbitrarySomething() is in order. But how would the arbitrary something be returned, for comparison later? Perhaps .ArbitrarySomething(out something) … gag. Generally though, I like what you are going for. I thought you would mention “batch” methods on the builder that set several fields at once, such as .InvoiceDueNextWeek() or something similar.

    jbrains’s post: http://blog.thecodewhisperer.com/post/333781027/what-your-tests-dont-need-to-know-will-hurt-you

    @Zach: Largely the Builder’s have mutable fields so that the objects that they build don’t need to. No “final” keywords are lost, because the fields would not be final anyway; they would be mutated by the methods of the fluent interface. But you seem to know this judging by the example in your comment.

    @Mark: With respect to the “null” value, you mean that you actually want it to specifically be null, right? Not that you want it to have some value but the actual value is unimportant, or that you want to avoid specifying it completely? If you want it to be specifically null, I think you should mention it in the method name. Programmers, who will be consuming these unit tests, know about null. Perhaps if there is a null-object pattern in play, then .WithNoFoo() would make sense, because it will do a “foo = Foo.None” assignment, or similar. On the other hand, saying .WithNoFoo() for null would capture and document the design decision that no foo is to be represented by null, at least for the moment.

    In the news, “null” remains a source of confusion.

  • http://www.markhneedham.com Mark Needham

    @Andy – yeh we do want a null. It’s actually for representing a non selected value coming in from a form that the user fills in. I guess we could use a functional Option type to do that but right now we’re not.

    @Dave – yeah I want specifically null in this case. Hadn’t thought of the batch methods actually, that’s a cool idea must try that.