| name | test-data-builders |
| description | Patterns for producing test data cleanly — builders, fixtures, and object mothers — across Kotlin, Swift, Dart, and TypeScript. Use when tests have noisy setup or when multiple tests drift on the "same" entity shape. |
Test Data Builders, Fixtures, and Object Mothers
Instructions
Tests fail for two reasons: the code is wrong, or the setup is wrong. A test that spends 30 lines building a User and 2 lines asserting behavior is hiding the behavior. Use builders, fixtures, and object mothers to push setup noise out of the test body.
1. Three Patterns, One Goal
- Fixture — a static, pre-shaped instance used as-is. Good for read-only, canonical shapes.
- Object Mother — a named factory that returns a realistic default for a concept (e.g.,
Users.anonymous(), Users.loyaltyGold()). Good when there are a few well-known "personas".
- Builder — a fluent API to tweak a default. Good when many tests need variations.
Combine them: an object mother returns a builder; tests tweak only the fields that matter.
2. Kotlin Builder
data class User(
val id: UserId,
val email: String,
val tier: Tier,
val createdAt: Instant,
)
class UserBuilder {
var id: UserId = UserId(1)
var email: String = "ada@example.com"
var tier: Tier = Tier.STANDARD
var createdAt: Instant = Instant.parse("2026-01-01T00:00:00Z")
fun build() = User(id, email, tier, createdAt)
}
fun aUser(block: UserBuilder.() -> Unit = {}) =
UserBuilder().apply(block).build()
val gold = aUser { tier = Tier.GOLD }
For Kotlin data classes, you can also lean on copy:
object Users {
val default = User(UserId(1), "ada@example.com", Tier.STANDARD, T0)
val gold = default.copy(tier = Tier.GOLD)
}
3. Swift Builder
struct User: Equatable {
var id: Int
var email: String
var tier: Tier
var createdAt: Date
}
enum Users {
static func make(
id: Int = 1,
email: String = "ada@example.com",
tier: Tier = .standard,
createdAt: Date = .t0
) -> User {
User(id: id, email: email, tier: tier, createdAt: createdAt)
}
}
let gold = Users.make(tier: .gold)
Default-argument "make" functions are idiomatic Swift and usually beat chained-builder APIs.
4. Dart Builder
class UserBuilder {
int id = 1;
String email = 'ada@example.com';
Tier tier = Tier.standard;
DateTime createdAt = DateTime.utc(2026, 1, 1);
User build() => User(id: id, email: email, tier: tier, createdAt: createdAt);
}
User aUser({void Function(UserBuilder)? config}) {
final b = UserBuilder();
config?.call(b);
return b.build();
}
final gold = aUser(config: (b) => b.tier = Tier.gold);
With freezed, prefer copyWith on a canonical instance.
5. TypeScript Builder
type User = { id: number; email: string; tier: Tier; createdAt: Date };
export const aUser = (overrides: Partial<User> = {}): User => ({
id: 1,
email: 'ada@example.com',
tier: 'standard',
createdAt: new Date('2026-01-01T00:00:00Z'),
...overrides,
});
The Partial<T> override pattern is the simplest durable builder in TS.
6. Object Mothers for Personas
object Users {
fun anonymous() = aUser { tier = Tier.ANON; email = "anon@example.com" }
fun loyaltyGold() = aUser { tier = Tier.GOLD }
fun loyaltySilver()= aUser { tier = Tier.SILVER }
}
Rule: if a persona appears in ≥ 3 tests, promote it to the mother. If it appears in 1, keep the tweak inline.
7. Fixture Files (JSON, etc.)
For API response shapes, commit testdata/ JSON fixtures and load them in tests. Keep filenames descriptive (orders/checkout_success.json) and version them with the schema. Re-use the same fixtures in unit, integration, and contract tests when the shape is identical.
8. Identity and Time
Centralize default IDs and timestamps. A T0 constant for time, a deterministic IdGenerator, and sequential UserId(1), UserId(2) IDs make assertions stable and failures diffable.
9. Anti-Patterns
- Random fakers (
Faker, faker-js) by default. Randomness hides ordering bugs; use only inside property-based tests with a seed.
- Deep-nested constructor chains in tests. If you see one, extract a builder.
- Setup that mutates a shared fixture. Each test owns its tweaks; shared mutable fixtures leak state.
10. Checklist