A fluent, flexible utility for building mock objects for testing in TypeScript/JavaScript. Supports static values, factories, arrays, nested objects, incrementing fields, templates, presets, and more.
- ποΈ Fluent API for building mock objects
- π’ Supports string, number, boolean, array, object, and incrementing fields
- π Generate single or multiple objects (
repeat
) - π§© Extend with templates or reusable presets
- π§ͺ Supports value factories (functions)
- πͺ TypeScript support with type casting for results
- πͺΆ Zero dependencies
- π‘οΈ Optional deep copy to prevent mutation between builds
- π‘οΈ Optional field validation (see
.build()
options) - β Custom field validators to ensure data integrity
npm install data-mock-builder
import { MockBuilder } from "data-mock-builder";
const builder = new MockBuilder().field("id").increment(1).field("name").string("Alice").field("active").boolean(true);
const result = builder.build();
console.log(result);
// { id: 1, name: "Alice", active: true }
Or with compacted field definition:
import { MockBuilder } from "data-mock-builder";
const builder = new MockBuilder().field("id", 1).field("name", "Alice").field("active", true);
const result = builder.build();
console.log(result);
// { id: 1, name: "Alice", active: true }
const users = new MockBuilder().field("id").increment(100).field("role").string("user").repeat(3).build();
console.log(users);
// [
// { id: 100, role: "user" },
// { id: 101, role: "user" },
// { id: 102, role: "user" }
// ]
Or with compacted field definition:
const users = new MockBuilder()
.field("id", () => Math.floor(Math.random() * 1000))
.field("role", "user")
.repeat(2)
.build();
console.log(users);
// [
// { id: 123, role: "user" },
// { id: 456, role: "user" }
// ]
// (ids will be random)
const builder = new MockBuilder()
.field("createdAt")
.number(() => Date.now())
.field("randomTag")
.string(() => Math.random().toString(36).slice(2, 8));
const result = builder.build();
console.log(result);
// { createdAt: 1680000000000, randomTag: "a1b2c3" } // values will vary
const addressBuilder = new MockBuilder().field("city").string("Paris").field("zip").number(75000);
const userBuilder = new MockBuilder()
.field("name")
.string("Alice")
.field("address")
.object(() => addressBuilder.build());
const user = userBuilder.build();
console.log(user);
// { name: "Alice", address: { city: "Paris", zip: 75000 } }
MockBuilder.definePreset("user", { name: "Bob", age: 25 });
const builder = new MockBuilder().preset("user").field("age").number(30); // override preset
const result = builder.build();
console.log(result);
// { name: "Bob", age: 30 }
// Define a preset for a product
MockBuilder.definePreset("product", { name: "Widget", price: 9.99 });
// Use the preset and override a field
const builder = new MockBuilder().preset("product").field("price").number(19.99);
const result = builder.build();
console.log(result);
// { name: "Widget", price: 19.99 }
const template = { foo: "bar", count: 42 };
const builder = new MockBuilder().extend(template).field("extra", true);
const result = builder.build();
console.log(result);
// { foo: "bar", count: 42, extra: true }
const builder = new MockBuilder().field("foo", 123).field("bar", () => "baz");
const result = builder.build();
console.log(result);
// { foo: 123, bar: "baz" }
const builder = new MockBuilder().field("n").increment(0, 5).repeat(3);
const result = builder.build();
console.log(result);
// [{ n: 0 }, { n: 5 }, { n: 10 }]
const builder = new MockBuilder()
.field("id")
.increment(1)
.field("name")
.string("User")
// Access index parameter in generator function
.field("displayName", (obj, index) => `${obj.name} ${index || 0}`)
// Access current object and control deep copy
.field("summary", (obj, index, options) => {
// options.deepCopy controls whether the returned value is deep copied
// options.skipValidation provides validation setting
return `ID: ${obj.id}, Name: ${obj.displayName}, Deep Copy: ${options?.deepCopy}`;
});
const users = builder.repeat(2).build();
console.log(users);
// [
// {
// id: 1,
// name: "User",
// displayName: "User 0",
// summary: "ID: 1, Name: User 0, Deep Copy: true"
// },
// {
// id: 2,
// name: "User",
// displayName: "User 1",
// summary: "ID: 2, Name: User 1, Deep Copy: true"
// }
// ]
const builder = new MockBuilder()
.field("age")
.number(25)
// Add validator using standalone method
.validator("age", (value) => ({
success: value >= 18,
errorMsg: "Age must be at least 18",
}))
.field("email")
.string("user@example.com")
// Add validator by chaining after field definition
.field("username")
.string("user123")
.validator((value) => ({
success: value.length >= 5,
errorMsg: "Username must be at least 5 characters",
}));
// By default, validation is skipped for performance
const user1 = builder.build();
// Enable validation by setting skipValidation to false
try {
// This would throw if any validation fails
const user2 = builder.build({ skipValidation: false });
console.log("All validations passed!");
} catch (error) {
console.error(error.message); // Contains all validation error messages
}
You can cast the result of .build()
to your interface or type for full type safety:
interface User {
id: number;
name: string;
active: boolean;
}
const builder = new MockBuilder().field("id").number(1).field("name").string("Alice").field("active").boolean(true);
const user = builder.build<User>();
// user is typed as User
const users = builder.repeat(2).build<User[]>();
// users is typed as User[]
By default, the builder deep-copies all field values to prevent mutation between builds.
You can disable deep copy globally or per build:
const builder = new MockBuilder().field("arr").array([1, 2]);
// Default: deep copy ON
const a = builder.build();
a.arr.push(3);
const b = builder.build();
console.log(b.arr); // [1, 2]
// Disable deep copy for all builds from this builder
builder.deepCopy(false);
const c = builder.build();
c.arr.push(4);
const d = builder.build();
console.log(d.arr); // [1, 2, 4]
// Or disable/enable deep copy per build:
const e = builder.build({ deepCopy: true }); // deep copy ON for this build
const f = builder.build({ deepCopy: false }); // deep copy OFF for this build
By default, field validation is skipped for performance and compatibility.
If you want to validate that all fields required by a type are present in the built object, use:
interface User {
id: number;
name: string;
}
const builder = new MockBuilder().field("id").number(1);
// This will NOT throw by default:
const user = builder.build<User>();
// To enable runtime validation:
try {
builder.build<User>({ skipValidation: false }); // Throws if any required field is missing
} catch (err) {
console.error(err);
}
Note: Due to TypeScript type erasure, runtime validation only works for plain objects, not for arrays of objects (e.g.,
User[]
).
For most use cases, type safety is enforced at compile time.
Method | Description |
---|---|
.field(name) |
Start defining a field. Chain with .string() , .number() , .boolean() , .array() , .object() , or .increment() . |
.field(name, value) |
Add a field directly with a static value or factory function. |
.validator(name, fn) |
Add a validator function for a field. Can return true or an error message string. |
.repeat(n) |
Generate n objects (returns an array from .build() ). |
.extend(template) |
Add fields from a plain object. |
.preset(name) |
Add fields from a named preset (see .definePreset ). |
.build<T>(options?) |
Build the object(s), optionally cast to type T . Options: { deepCopy?: boolean; skipValidation?: boolean } |
.deepCopy(enabled) |
Enable or disable deep copy for all subsequent builds from this builder. |
.increment(start = 1, step = 1) |
Incrementing number field. |
.definePreset(name, template) |
Define a reusable preset. |
MIT Β© jaktestowac.pl
Powered by jaktestowac.pl team.
π Check out GitHub profile for more open-source projects and resources.