Senior SDETFrameworks & Design PatternsFree question
How do you use the Builder pattern for test data?
60-second answer
The Builder pattern creates complex objects step by step with readable, chainable methods and sensible defaults. For test data it means each test specifies only what matters to it, like "a premium user with an expired card", while everything else gets valid defaults, including unique emails. This removes huge constructors and copy-pasted JSON, makes intent obvious, and when the model gets a new mandatory field I add one default in the builder instead of fixing hundreds of tests. Builders pair well with an API or DB step that actually creates the data.
Explanation
- Defaults should be valid and unique (UUIDs, timestamps) for parallel safety.
- Named factory methods (
aUser(),anAdmin()) improve readability. - Combine with Faker for realism, but seed it for reproducibility.
- Lombok
@Builder(toBuilder = true)or Java records with a manual builder. - "Object Mother" is the related pattern of predefined personas.
Example
UserBuilder.aUser().inCity("Pune").withOrders(3).create(api) creates a realistic customer with three delivered orders in one line.
Sample code
public final class UserBuilder {
private String email = "qa+" + UUID.randomUUID() + "@test.local";
private String city = "Bengaluru";
private boolean premium = false;
private int orders = 0;
public static UserBuilder aUser() { return new UserBuilder(); }
public UserBuilder premium() { this.premium = true; return this; }
public UserBuilder inCity(String c) { this.city = c; return this; }
public UserBuilder withOrders(int n) { this.orders = n; return this; }
public User create(UserApi api) {
User u = api.create(new CreateUser(email, city, premium));
for (int i = 0; i < orders; i++) api.createDeliveredOrder(u.id());
return u;
}
}
User user = UserBuilder.aUser().premium().withOrders(3).create(userApi);