Question

I've started to construct a builder so that I can easily create test data for unit tests I am writing.

The basic structure of the builder is:

public class MyClassBuilder
{
    public int id = 0; //setting the default value here

    //allows MyClass to be built with a specific id
    public MyClassBuilder WithId(int id)
    {
        this.id = id;
        return this;
    }

    public MyClass Build()
    {
        return new MyClass(id);
    }
}

The usage of this pattern then becomes:

MyClass mc = new MyClassBuilder().WithId(5).Build();

I'm comfortable with that...but where I have questions is when MyClass has a property that is non trivial....I'm a little uncertain about how to go about constructing it with a default value.

public class MyClassBuilder
{
    public int id = 0; //setting the default value here

    //do I construct the default value using a MySecondClassBuilder?
    public MySecondClass mySecondClass;

    //allows MyClass to be built with a specific id
    public MyClassBuilder WithId(int id)
    {
        this.id = id;
        return this;
    }

    public MyClassBuilder WithMySecondClass(MySecondClass mySecondClass)
    {
        this.mySecondClass = mySecondClass;
    }

    public MyClass Build()
    {
        return new MyClass(id);
    }
}

My assumption is that I would create a builder for MySecondClass and use that to create the default implementation.

Can anyone confirm that my assumption is correct and is the best practice?

I'm currently in the process of testing out my assumption but I thought I'd document this idea on StackOverflow since the only examples of the builder patter that I could find using google only ever constructed properties that are value types and not reference types.

Was it helpful?

Solution

As with most things - it depends.

If you want reference semantics in the mySecondClass field (ie: you want to save a reference to another class), then it's exactly like your integer. Having the default be null could be perfectly acceptable.

However, if you want to always guarantee a value, you'll need to either build or just construct a new object for your second class.

Also, if you want cloning instead of by reference copies, you'd need to construct a new object and assign it to the reference.

That being said, in many cases, traditional constructors are more appropriate than trying to do a fluent build-style interface. It's really easy to "mess up" a fluent interface for building like this, since you're relying on the user more than with a traditional constructor.

As an example, in your code, what happens if the user never calls Build? It's still returning something (since each of the methods returns an object), but is it valid? I wouldn't be sure as a consumer of your class. Traditional constructors with overloads, on the other hand, show me exactly what is valid and what is not valid, and are compile-time enforced.

OTHER TIPS

Once I have a complex object where I want a dummy to use in unit tests, I'll generally turn to a mocking/faking/isolation framework. In C#, my preference is Moq, but there are several out there, like Rhino Mocks, Typemock and so on.

The benefit there is that they will typically have the capability to stub out all properties that exist in the original type with default values and empty objects, without you having to do anything other than a single line statement to create the fake object. At the same time, you can easily configure the object to return the values you want for those of the properties that matter to your test. Also, most such frameworks support faking recursively, so that the complex objects inside your type will also get faked, and so on.

There are reasons why one might not want to use a separate framework, in which case I think Reed Copsey's suggestion would be the manual way to do it.

If there aren't any reasons to avoid adding a mocking framework (which would only be a dependency to your test projects, assuming you've split testing out), I would heartily recommend it for complex types.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top