views:

159

answers:

2

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.

A: 

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.

Reed Copsey
All I'm wanting to use these builders for is for creating objects for use within my unit tests so that I can know that they are fully constructed and won't throw null reference exceptions when being used by parts of the code I'm not testing. Does that clarify things a little?
mezoid
Not really. That's one of the main reasons to use traditional constructors with overloads - a normal, standard constructor will ALWAYS leave the object in a usable state (provided the class is well-designed), wheras builders like this can cause all sorts of issues. If your classes are that way, your unit tests can just construct them using their standard constructors. How is this simpler than a standard constructor (plus optional object initializer)? http://msdn.microsoft.com/en-us/library/bb384062.aspx
Reed Copsey
Well, the class I'm trying to construct is a class that is auto generated for use by NHibernate to represent one of the tables in the database. It has over 30 parameters for the constructor which makes constructing objects for use in unit tests very tedious...especially since I have to manually construct many parameters that I really don't care about.
mezoid
Personally, I'd just make a class or struct with all options as properties, and set the properties, and have one constructor or build method that built the class off of that single struct/class. It's easier, and simpler in terms of code, plus, gives you good control over default params.
Reed Copsey
A: 

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.

McMuttons