Test data builders
Skill almasumdev/awesome-mobile-testing-agent-skills/.github/skills/unit/test-data-builders
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.From its SKILL.md
npx -y skills add almasumdev/awesome-mobile-testing-agent-skills --skill test-data-buildersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.8 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it
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 = "[email protected]"
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()
// Usage
val gold = aUser { tier = Tier.GOLD }
For Kotlin data classes, you can also lean on copy:
object Users {
val default = User(UserId(1), "[email protected]", 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 = "[email protected]",
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 = '[email protected]';
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: '[email protected]',
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 = "[email protected]" }
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
- Tests contain only the field values that matter for the assertion.
- Personas with repeated use are promoted to object mothers.
- Builders have sensible deterministic defaults for every field (time, id, locale).
- JSON fixtures live under
testdata/and are shared across test layers. - No random values leak into example-based tests.
- Builders return immutable objects; they do not mutate a shared instance.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.