Fluent Builders are extremely useful. Once you start using them there is no going back!

In my first article I outlined fluent builders – what they look like and how to put them together. This time I will go a little deeper to show you just how versatile they can be.

Method Naming

You can be as flexible as you like in naming the methods in your builders. A standard starting place is to use ‘With…’ for example WithFirstName(..), WithDateOfBirth(..) etc. I find the best naming convention is one that makes your tests as easy to read as possible.  For instance

Customer testCustomer = new CustomerBuilder().
    .WithName("Peter Perfect")
    .Aged(32)
    .ThatIsAFirstTimeBuyer();

Remember that it is fine to add specific methods such as ‘ThatIsAFirstTimeBuyer()’ above where it could be setting an individual property, a series of properties or calling further methods within the customer class to create that state. You can also add specific tests for your builders to ensure those methods are working as expected.

Adding Lists

Your builder methods can also take in lists or add to lists. Let’s say that our CustomerBuilder allows us to add products they have bought.  It could be created in two ways;

public CustomerBuilder WithProduct(Product product)
{
    this.customer.Products.Add(product);
    return this;
}

or

public CustomerBuilder WithProducts(List products)
{
    this.customer.Products = products;
    return this;
}

In the first case we could use a product builder to add each product to the customer

Customer customer = new CustomerBuilder()
    .WithProduct(new ProductBuilder().WithName("Apple")))
    .WithProduct(new ProductBuilder().WithName("Orange"))

In the second we could create a specific ProductListBuilder()

public class ProductListBuilder
{
    private readonly List products;

    public ProductListBuilder()
    {
        this.products = new List();
    }

    public static implicit operator List(ProductListBuilder builder)
    {
        return builder.products;
    }

    public ProductListBuilder WithProduct(string productName)
    {
        this.products.Add(new Product { Name = productName });
        return this;
    }
}

Which we would use this way

    List<Product> products = new ProductListBuilder().WithProduct("Apple").WithProduct("Orange");
    Customer customer = new CustomerBuilder().WithProducts(products);

Builders for Mocked Objects

Do you often have to mock repositories?  How about using a builder to do it?

public class MockCustomerRepositoryBuilder
{
    private readonly Mock<irepository> mockCustomerRepository;

    public MockCustomerRepositoryBuilder()
    {
        this.mockCustomerRepository= new Mock<irepository>();
    }

    public Customer SavedCustomer { get; private set; }

    public static implicit operator Mock<irepository>(MockCustomerRepositoryBuilder builder)
    {
        return builder.mockCustomerRepository;
    }

    public MockCustomerRepositoryBuilder WhereGetByIdReturns(Customer customer)
    {
        this.mockCustomerRepository.Setup(cfr => cfr.GetById(It.IsAny())).Returns(customer);
        return this;
    }

    public MockCustomerRepositoryBuilder WithSave()
    {
        this.mockCustomerRepository.Setup(cfr => cfr.Save(It.IsAny())).Callback(cfr => { SavedCustomer = cfr; });
        return this;
    }
}

The usage is as follows

    var mockCustomerRepository = new MockCustomerRepositoryBuilder()
        .WithSave()
        .WhereGetByIdReturns(new Customer().WithName("Tom Test"));

What is really cool about this is that you can set up the save method in your mocked repository to save whatever you pass in to a public property. This allows you to assert that your code under test has actually passed the right object through.  You can also direct it to return whatever you like from the GetById() method of this repository so that you don’t have to set it up in each test.

I hope this has helped you to further appreciate the power of these builders.  Please feel free to ask any questions you may have below.  Next time I will show how to test MVC controllers with builders in a fluent way.

Thanks for listening.

SHARE IT:

Commenting area

  1. Builders are great for reducing API documentation. But I think in this situation u should actually be switching to a mocking library that supports your workflow in a fluent manner, instead of using Moq just becasue Microsoft has included it as a default option.

    NSubstitute
    AutoFixture
    FluentAssertions

    var id = testparam;
    var customer = new Fixture().Create();
    var mockRepo = Substitute.For();
    mockRepo.GetById(id).Returns(customer)

    var sut = new Sut(mockRepo);
    var result = sut.Action();

    result.Should.Be(customer, “the customer should be located”);

    • Thanks for your comment Weq.
      NSubstitute, AutoFixture and FluentAssertions are all excellent tools – especially FluentAssertions which I will be writing a blog about soon. I used Moq here as it is generally well known and I didn’t want to confuse or detract from the main article.

      I would usually use bespoke builders over autofixture though as I find them more specific and they allow me to quickly (and legibly) create expected classes for comparison against returned classes in a test. I use FluentAssertions to make the comparison.

      Darren

Leave a Reply