| name | dotnet-testing-test-data-builder-pattern |
| description | Test Data Builder Pattern complete implementation guide. Used when using builder pattern to create maintainable test data or simplify complex object test preparation. Covers fluent interface, semantic methods, default value design, and Builder composition patterns.
Keywords: test data builder, builder pattern test, test data builder, object mother, fluent interface, fluent interface, UserBuilder, ProductBuilder, .With(), .Build(), AUser(), test data preparation, complex object creation, semantic testing
|
Source: kevintsengtw/dotnet-testing-agent-skills (MIT). Ported into dotnet-agent-harness.
Test Data Builder Pattern
Applicable Scenarios
Test Data Builder Pattern is a Builder Pattern variant specifically designed for testing, used to create clear,
maintainable, and semantically explicit test data. This pattern is especially suitable for handling complex objects with
multiple attributes, making test code more readable and reducing maintenance costs.
Core Concepts
What is Test Data Builder Pattern?
Test Data Builder Pattern is an improved version of Object Mother Pattern, mainly solving the following problems:
- Fixed test data problem: Object Mother provides fixed test objects, difficult to adjust for specific test
scenarios
- Unclear test intent: When creating objects directly, test focus is easily obscured by large amounts of attribute
settings
- Repeated code: Similar object creation logic repeated in multiple tests
Why Need Builder Pattern?
Traditional test data creation problems
Too many parameter settings, unclear test intent.
Using Builder Pattern improvement
Intent is explicit, only set properties test cares about.
Implementation Guide
Basic Builder Structure
A standard Test Data Builder should contain:
- Default values: Provide reasonable defaults for all necessary properties
- Fluent interface: Use
With* method chain to set properties
- Semantic methods: Provide meaningful default creators (like
AnAdminUser(), ARegularUser())
- Build method: Finally create and return target object
Complete Builder Example
public class UserBuilder
{
private string _name = "Default User";
private string _email = "default@example.com";
private int _age = 25;
private List<string> _roles = new();
private UserSettings _settings = new()
{
Theme = "Light",
Language = "en-US"
};
private bool _isActive = true;
private DateTime _createdAt = DateTime.UtcNow;
public UserBuilder WithName(string name)
{
_name = name;
return this;
}
public UserBuilder WithEmail(string email)
{
_email = email;
return this;
}
public UserBuilder WithAge(int age)
{
_age = age;
return this;
}
public UserBuilder WithRole()
{
_roles.Add(role);
;
}
{
_roles.AddRange(roles);
;
}
{
_isActive = ;
;
}
=> ();
=> UserBuilder()
.WithRoles(, );
=> UserBuilder()
.WithRole();
{
User
{
Name = _name,
Email = _email,
Age = _age,
Roles = _roles.ToArray(),
Settings = _settings,
IsActive = _isActive,
CreatedAt = _createdAt
};
}
}
```text
```csharp
[]
{
adminUser = UserBuilder
.AnAdminUser()
.WithName()
.WithEmail()
.WithAge()
.Build();
userService = UserService();
result = userService.CreateUser(adminUser);
Assert.NotNull(result);
Assert.Equal(, result.Name);
Assert.Contains(, result.Roles);
}
```text
```csharp
{
[]
[]
{
validator = UserValidator();
result = validator.IsValid(user);
Assert.Equal(expected, result);
}
IEnumerable<[]> GetUserScenarios()
{
[]
{
UserBuilder.AUser()
.WithName()
.WithEmail()
.WithAge()
.Build(),
};
[]
{
UserBuilder.AUser()
.WithName()
.Build(),
};
}
}
```text
Good practice: defaults make valid state.
Good practice: method names express test intent.
Good practice: Builders can be combined.
Keep Builder simple, dons Testing Practice - Day Challenge