Skip to main content

Developer Kit

Test Data Builder

Generates language-native test data factories with edge cases, nulls, extremes, and locale variations for any data model. Useful for building tests that reflect real-world inputs instead of toy fixtures. Engineers writing tests for data-handling code, QA engineers building test harnesses, localization teams ensuring non-English data paths work, security teams adding adversarial input coverage. "John Doe", age 30, email "john@example.com" — that do not reflect real-world data. The bugs that slip past these tests are the ones triggered by long names, unicode, trailing whitespace, empty strings, nulls, extreme numeric values, non-English locales, and adversarial inputs. A structured test data builder produces composable factories that make it easy to express "a typical user" plus "a user with a 200-character name in Japanese" in one line, which is the pattern that actually surfaces bugs before production.

Nexus CertifiedClaude CodeCodexOpenClaw
testingfixturestest-datafactoriesquality

One-Time Purchase

$19.99

Sample Output

Test Data Builder — Order Model

Language: TypeScript · Test framework: Vitest · Factory library: @faker-js/faker v9


📄 src/test/factories/order.factory.ts

/**
 * Order Factory
 * =============
 * Composable test-data builder for the `Order` domain model.
 *
 * USAGE
 * -----
 * import { orderFactory } from '@/test/factories/order.factory';
 *
 * // Single base order (happy path)
 * const order = orderFactory.build();
 *
 * // With a trait
 * const pending = orderFactory.build('withPendingStatus');
 * const huge    = orderFactory.build('withMaxLineItems');
 *
 * // Override individual fields
 * const order = orderFactory.build('withPendingStatus', { customerId: 'cust_abc123' });
 *
 * // Build a list
 * const orders = orderFactory.buildList(5, 'withCompletedStatus');
 *
 * // Deterministic / reproducible (seed before each test suite)
 * import { faker } from '@faker-js/faker';
 * beforeEach(() => faker.seed(42));
 *
 * EXTENDING
 * ---------
 * Add a new trait by appending to the `traits` map below.
 * Trait names must be camelCase and start with "with" or "as".
 * Traits are shallow-merged onto the base; deep-merge helper
 * `deepMerge()` is provided for nested objects.
 *
 * CONSTRAINTS RESPECTED
 * ---------------------
 * - `total` is always ≥ 0 and equals sum(lineItems[].subtotal)
 * - `placedAt` is never in the future on the base factory
 * - `shippedAt` is always after `placedAt` when present
 * - `status` transitions are semantically consistent per trait
 */

import { faker } from '@faker-js/faker';
import type { Order, LineItem, Address } from '@/domain/order';

// ─── helpers ────────────────────────────────────────────────────────────────

function buildLineItem(overrides: Partial<LineItem> = {}): LineItem {
  const qty      = faker.number.int({ min: 1, max: 10 });
  const price    = faker.number.float({ min: 0.01, max: 999.99, fractionDigits: 2 });
  return {
    id:          faker.string.uuid(),
    productId:   `prod_${faker.string.alphanumeric(10)}`,
    description: faker.commerce.productName(),
    quantity:    qty,
    unitPrice:   price,
    subtotal:    parseFloat((qty * price).toFixed(2)),
    ...overrides,
  };
}

function buildAddress(overrides: Partial<Address> = {}): Address {
  return {
    line1:      faker.location.streetAddress(),
    line2:      faker.helpers.maybe(() => faker.location.secondaryAddress()) ?? null,
    city:       faker.location.city(),
    state:      faker.location.state({ abbreviated: true }),
    postalCode: faker.location.zipCode(),
    country:    'US',
    ...overrides,
  };
}

function sumLineItems(items: LineItem[]): number {
  return parseFloat(items.reduce((acc, i) => acc + i.subtotal, 0).toFixed(2));
}

// ─── base factory ───────────────────────────────────────────────────────────

function buildBase(overrides: Partial<Order> = {}): Order {
  const lineItems  = [buildLineItem(), buildLineItem()];
  const placedAt   = faker.date.recent({ days: 30 });
  const shippedAt  = faker.date.between({ from: placedAt, to: new Date() });

  return {
    id:              `ord_${faker.string.alphanumeric(12)}`,
    customerId:      `cust_${faker.string.alphanumeric(10)}`,
    status:          'completed',
    lineItems,
    total:           sumLineItems(lineItems),
    currency:        'USD',
    shippingAddress: buildAddress(),
    billingAddress:  buildAddress(),
    placedAt,
    shippedAt,
    deliveredAt:     faker.date.between({ from: shippedAt, to: new Date() }),
    cancelledAt:     null,
    notes:           null,
    metadata:        {},
    schemaVersion:   1,
    ...overrides,
  };
}

// ─── traits ─────────────────────────────────────────────────────────────────

const traits: Record<string, (base: Order) => Partial<Order>> = {

  /** Order placed but not yet actioned by fulfillment */
  withPendingStatus: () => ({
    status:      'pending',
    shippedAt:   null,
    deliveredAt: null,
    cancelledAt: null,
  }),

  /** Payment confirmed; awaiting warehouse pick */
  withConfirmedStatus: () => ({
    status:      'confirmed',
    shippedAt:   null,
    deliveredAt: null,
    cancelledAt: null,
  }),

  /** Fully delivered — the canonical happy-path end-state */
  withCompletedStatus: (base) => ({
    status:      'completed',
    shippedAt:   faker.date.between({ from: base.placedAt, to: new Date() }),
    deliveredAt: faker.date.recent({ days: 7 }),
    cancelledAt: null,
  }),

  /** Cancelled before shipment — cancelledAt must be populated */
  withCancelledStatus: (base) => ({
    status:      'cancelled',
    shippedAt:   null,
    deliveredAt: null,
    cancelledAt: faker.date.between({ from: base.placedAt, to: new Date() }),
  }),

  /** Max schema-allowed line items (stress-tests rendering & subtotal math) */
  withMaxLineItems: () => {
    const items = Array.from({ length: 100 }, () => buildLineItem());
    return { lineItems: items, total: sumLineItems(items) };
  },

  /** Single line item — minimum viable order */
  withSingleLineItem: () => {
    const items = [buildLineItem()];
    return { lineItems: items, total: sumLineItems(items) };
  },

  /** Zero-value order — free promotional item, total must be 0 */
  withZeroTotal: () => {
    const items = [buildLineItem({ unitPrice: 0, subtotal: 0 })];
    return { lineItems: items, total: 0 };
  },

  /** Very old order — exercises date-range filters and archival logic */
  withAncientPlacedDate: () => ({
    placedAt:    new Date('1970-01-02T00:00:00.000Z'),
    shippedAt:   new Date('1970-01-03T00:00:00.000Z'),
    deliveredAt: new Date('1970-01-05T00:00:00.000Z'),
  }),

  /** Placed moments ago — exercises "just now" display logic */
  withRecentPlacedDate: () => ({
    placedAt:    new Date(Date.now() - 5_000),
    shippedAt:   null,
    deliveredAt: null,
    status:      'pending',
    cancelledAt: null,
  }),

  /** Notes field at max schema length (500 chars) */
  withMaxLengthNotes: () => ({
    notes: 'A'.repeat(500),
  }),

  /** Unicode in free-text fields — RTL, emoji, combining characters */
  withUnicodeNotes: () => ({
    notes: '日本語テスト 🛒 \u202Emailto:test\u202C \u0301combining café',
  }),

  /**
   * Adversarial inputs — exercise XSS / SQLi surface area.
   * Use on any endpoint that echoes order data back into HTML or raw SQL.
   */
  withAdversarialInputs: () => ({
    notes:       `<script>alert('xss')</script><img src=x onerror=alert(1)>`,
    metadata:    {
      ref:       `'; DROP TABLE orders; --`,
      callback:  `javascript:void(document.cookie)`,
      unicode:   `\u200Badmin\u200B`,           // zero-width spaces around "admin"
      overlong:  '\xC0\xAF',                    // overlong UTF-8 slash
    },
    shippingAddress: buildAddress({
      line1: `<svg/onload=alert(1)>`,
      city:  `Robert'); DROP TABLE cities;--`,
    }),
  }),

  /** Non-US locale — CA postal code, CAD currency */
  withCanadianLocale: () => ({
    currency:        'CAD',
    shippingAddress: buildAddress({
      postalCode: faker.helpers.fromRegExp(/[A-Z][0-9][A-Z] [0-9][A-Z][0-9]/),
      state:      'ON',
      country:    'CA',
    }),
  }),

};

// ─── public API ─────────────────────────────────────────────────────────────

type TraitName = keyof typeof traits;

export const orderFactory = {
  build(trait?: TraitName, overrides: Partial<Order> = {}): Order {
    const base        = buildBase();
    const traitPatch  = trait ? traits[trait](base) : {};
    return { ...base, ...traitPatch, ...overrides };
  },

  buildList(count: number, trait?: TraitName, overrides: Partial<Order> = {}): Order[] {
    return Array.from({ length: count }, () => this.build(trait, overrides));
  },

  /** Expose helpers so tests can compose custom addresses/line-items inline */
  helpers: { buildLineItem, buildAddress },
};

🧪 Example Usage (matching project's existing test style)

// src/modules/orders/__tests__/order-summary.service.test.ts

import { describe, it, expect, beforeEach } from 'vitest';
import { faker } from '@faker-js/faker';
import { orderFactory } from '@/test/factories/order.factory';
import { OrderSummaryService } from '../order-summary.service';

describe('OrderSummaryService', () => {
  beforeEach(() => faker.seed(42)); // deterministic run

  it('formats a completed order correctly', () => {
    const order = orderFactory.build('withCompletedStatus');
    const summary = OrderSummaryService.format(order);
    expect(summary.statusLabel).toBe('Delivered');
    expect(summary.total).toMatch(/^\$[\d,]+\.\d{2}$/);
  });

  it('shows no ship date for pending orders', () => {
    const order = orderFactory.build('withPendingStatus');
    expect(OrderSummaryService.format(order).shippedDate).toBeNull();
  });

  it('handles 100 line items without truncation', () => {
    const order = orderFactory.build('withMaxLineItems');
    const summary = OrderSummaryService.format(order);
    expect(summary.lineItemCount).toBe(100);
  });

  it('does not render HTML from adversarial note inputs', () => {
    const order = orderFactory.build('withAdversarialInputs');
    const html  = OrderSummaryService.renderNotesHtml(order.notes!);
    expect(html).not.toContain('<script>');
    expect(html).not.toContain('onerror=');
  });

  it('displays CAD currency symbol for Canadian orders', () => {
    const order = orderFactory.build('withCanadianLocale');
    expect(OrderSummaryService.format(order).currencySymbol).toBe('CA$');
  });
});

🗺️ Migration Guide — Replacing Inline Fixtures

Before — brittle inline object repeated across 14 test files:

const mockOrder = {
  id: 'ord_test123',
  customerId: 'cust_abc',
  status: 'completed',
  lineItems: [{ id: 'li_1', productId: 'prod_x', quantity: 2, unitPrice: 9.99, subtotal: 19.98 }],
  total: 19.98,
  currency: 'USD',
  // ... 12 more fields hardcoded, shippedAt missing, schema v0
};

After — one line, always schema-valid, readable intent:

const order = orderFactory.build('withCompletedStatus');

Step-by-step migration:

| Step | Action | |------|--------| | 1 | Add faker.seed(42) inside beforeEach in every affected suite | | 2 | Replace full inline objects with orderFactory.build() | | 3 | Replace status-specific fakes with the matching trait ('withPendingStatus', etc.) | | 4 | Where a test asserts on a specific field value, pass it as the override argument: orderFactory.build('withCompletedStatus', { customerId: 'cust_abc' }) | | 5 | Delete the old mockOrder const and any as Order casts masking missing fields | | 6 | Run vitest --reporter=verbose — all tests should pass; any failures reveal fields the factory exposes that the inline fixture was silently hiding |

Tip: If a test breaks after migration, that's a signal — the inline fixture was masking a real constraint violation. Fix the test logic, not the factory.

View full sample →

All sales final. No refunds on digital products.

Includes support for Claude Code, Codex, and OpenClaw in the same license.

What You Get With This Skill

Generates language-native test data factories with edge cases, nulls, extremes, and locale variations for any data model. Useful for building tests that reflect real-world inputs instead of toy fixtures.

All ClearPoint Nexus Skills Include

  • Production-ready workflow packaging for three supported platforms.
  • Reusable structure designed for repeatable operator tasks.
  • Clear deliverable format, not just raw prompt output.

Related Skills

Developer Kit
Featured
Code Generation
Generates, reviews, debugs, and executes code in sandboxed workflows. Useful for implementation, refactoring, and technical problem solving.
Claude CodeCodexOpenClaw
codingdebuggingcode-review

$19.99

One-time license

View Skill
Developer Kit
API Documentation Generator
Generates structured, developer-ready API documentation from code, OpenAPI specs, route definitions, or descriptions. Produces reference docs, quickstart guides, error references, and code examples.
Claude CodeCodexOpenClaw
apidocumentationdeveloper-experience

$19.99

One-time license

View Skill
Developer Kit
Intelligent PR Composer
Generates pull request descriptions that capture context, alternatives considered, test plan, risk areas, and reviewer guidance beyond a simple diff summary. Useful for teams that want senior-quality PRs without manual authoring.
Claude CodeCodexOpenClaw
pull-requestscode-reviewgit

$19.99

One-time license

View Skill