Skip to main content

DevOps & Security

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 CodeCodexOpenClawGoogle Antigravity
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

What this is

A composable factory for the Order domain model with 12 traits that exercise the cases inline fixtures usually miss: empty states, max line items, unicode, RTL characters, adversarial XSS/SQLi inputs, ancient dates, "just now" dates, and non-US locales. Seeded for reproducibility (faker.seed(42)) so failing tests give the same output across machines.

Trait families

Status — pending, confirmed, completed, cancelled4 traits
Cardinality — single item, max items (100), zero-value order3 traits
Time — ancient placed date, just-now placed date2 traits
Content — max-length notes, unicode notes, adversarial inputs3 traits
Locale — Canadian (CAD currency, CA postal code regex)1 trait

📄 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:

StepAction
1Add faker.seed(42) inside beforeEach in every affected suite
2Replace full inline objects with orderFactory.build()
3Replace status-specific fakes with the matching trait ('withPendingStatus', etc.)
4Where a test asserts on a specific field value, pass it as the override argument: orderFactory.build('withCompletedStatus', { customerId: 'cust_abc' })
5Delete the old mockOrder const and any as Order casts masking missing fields
6Run 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.

Adversarial trait — handle with care

The withAdversarialInputs trait deliberately injects XSS, SQLi, and unicode bidi attacks into the fixture. Use it only on tests that exercise output sanitization or input validation; using it on a happy-path test will produce false failures. Never feed an adversarial fixture into a fixture-comparing snapshot test — the snapshot itself becomes a payload carrier.

Determinism gotcha

faker.seed(42) makes runs reproducible, but only if every call to the factory in a test is in the same order. If you call orderFactory.build() inside a describe.each loop and the framework runs them in parallel, the seed is shared and outputs interleave. Either move the seed call inside the loop or pin the test runner to serial mode for that suite.


Generated by the ClearPoint Nexus Test Data Builder skill. Extend with project-specific traits as you find bug classes the existing traits miss.

This sample illustrates the skill's output format. Names, metrics, and operational details are illustrative unless the artifact explicitly analyzes public information.

View full sample →

All sales final. No refunds on digital products.

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

Also in Testing & QA

Bundle price: $55. Compare this skill with the full workflow bundle or Pro access.

Best for

Engineering teams writing or refreshing test fixtures (unit, integration, end-to-end) who want language-native factory builders with edge cases (nulls, extremes, locales, special characters) rather than a flat block of test data, platform teams standardizing test-data patterns across services, and QA leads catching test-fragility issues that come from inconsistent fixtures across the suite. Most valuable on data models with non-trivial structure (nested objects, optional fields, locale-sensitive fields) where hand-built fixtures consistently miss the edge cases.

Not ideal for

Trivial data models where hand-built fixtures take minutes. Also a poor fit as a substitute for production-like test data; factories produce structurally diverse fixtures but synthetic data still differs from the messiness of real production data.

Included in this purchase

  • Claude Code, Codex, OpenClaw, and Google Antigravity skill files.
  • Setup guidance for the right adapter in your workspace.
  • One-time license for the purchased skill version.

Setup

Plan for a short setup in the repository or workspace where the skill will run. Some coding familiarity helps for implementation-heavy outputs.

Claude CodeCodexOpenClawGoogle Antigravity

Related Skills

Incident Response
Outage Response Playbook
Generates structured, role-clear incident response playbooks for specific failure scenarios. Covers detection through resolution and post-mortem — ready to use when an incident actually happens.
Claude CodeCodexOpenClawGoogle Antigravity
outage-responsereliabilityrunbooks

$19.99

One-time license

View Skill
Incident Response
Incident Postmortem Writer
Generates a structured blameless postmortem from incident timelines, alerts, and deploy logs with root cause analysis, impact assessment, and owned action items. Useful for producing first-draft postmortems under operational pressure.
Claude CodeCodexOpenClawGoogle Antigravity
postmortemsincident-responseoperations

$19.99

One-time license

View Skill
Security Scanning
OWASP Top 10 Scanner
Scans code for OWASP Top 10 vulnerability patterns including injection, XSS, IDOR, and insecure deserialization with severity ratings and remediation snippets. Useful for pre-commit security checks and enterprise compliance.
Claude CodeCodexOpenClawGoogle Antigravity
securityowaspvulnerabilities

$19.99

One-time license

View Skill

Future Updates

This purchase includes the current version of the skill. If you want future adapter updates — meaning compatibility and packaging updates as supported platforms evolve — plus new catalog additions included automatically, upgrade to Pro.

Upgrade to Pro