Course contentsShow
← C#
Lesson 1720 of 1,90815. Software Quality and TestingFree lesson

Reducing Test Setup Complexity

Apply builder and factory patterns within tests to streamline arrangement.

In this lesson, you’ll learn how to replace cumbersome, multi-line test arrangements with the Builder and Factory patterns to keep your test suites maintainable and readable.

As your domain models grow in complexity, the "Arrange" phase of your tests often becomes a liability. If creating a User entity requires setting up five dependencies, three nested objects, and a dozen property assignments, every single test will break the moment you add a new mandatory field to that class.

The Builder Pattern for Flexible Setup

Instead of manually instantiating complex objects, create a TestBuilder class. This allows you to define sensible defaults and only override the specific properties relevant to the test case at hand.

public class UserBuilder 
{
    private string _name = "Default User";
    private Role _role = Role.Member;

    public UserBuilder WithAdminRole() { _role = Role.Admin; return this; }
    public User Build() => new User(_name, _role);
}

// In your test:
var user = new UserBuilder().WithAdminRole().Build();

By using the fluent interface, the test intent becomes clear: "I am testing an Admin," while hiding the boilerplate of how an Admin is actually constructed.

The Factory Pattern for Common Scenarios

While the Builder is great for varying internal state, the Factory pattern excels at creating "known good" complex object graphs. If your tests frequently require an Order that is already in a "Shipped" state with associated LineItems and AuditLogs, don't write that setup code inside the test method.

Instead, move it to a TestDataFactory:

public static class OrderFactory 
{
    public static Order CreateShippedOrder() 
    {
        var order = new Order();
        order.AddLineItem(new Product("Test Item"), 1);
        order.MarkAsShipped();
        return order;
    }
}

By abstracting these arrangements, you achieve two things: first, your test methods focus exclusively on the specific behavior being verified. Second, when your domain model inevitably changes, you only update the Builder or Factory in one place, rather than updating dozens of brittle test files.

Key Takeaway: Use Builders for granular configuration and Factories for common object graphs to encapsulate setup logic, keeping your tests resilient to domain model changes.