Skip to main content

Test Data & Mock Generation

Reference
⏱ 15 min🟡 Intermediate

Test data and mock factories are areas where Copilot shines — they are repetitive, type-driven, and rarely contain complex logic. This step shows how to generate fixtures and stubs responsibly, including the most important guardrail: never letting real-looking sensitive data into your codebase.

🙋Audience Poll

When Copilot generates fixtures, what is your first review priority?

🔒Secret and PII safety
📐Type correctness
🧩Override flexibility
⚖️Realism vs maintainability balance

There is no single right answer. Use team context and risk level.

Test Data Strategy
📐Type Definitions
context for▸
🤖Copilot
generates▸
🏭Factory Functions
🔌Mock Stubs
🧪Test Files

Part A — Fixture Factories for the Checkout Pipeline​

A factory function creates test objects with sensible defaults that can be overridden per test. Ask Copilot to generate one from your type. Here the factories mirror the checkout domain used throughout this workshop:

Generate TypeScript factory functions for the checkout pipeline types.
#file:src/models/cart.ts
#file:src/models/order.ts

Factories needed:
1. buildCartItem(overrides?) — CartItem with id, productId, name, price, quantity
2. buildOrder(overrides?) — Order with id, userId, items, subtotal, status ('pending'|'confirmed'|'cancelled')

Use obviously fake defaults. Make all fields overridable via a spread parameter.
// tests/factories.ts (Copilot-generated — matches checkout domain)
export const buildCartItem = (overrides: Partial<CartItem> = {}): CartItem => ({
id: 'item-1',
productId: 'prod_1',
name: 'Workshop T-Shirt',
price: 25,
quantity: 1,
...overrides,
});

export const buildOrder = (overrides: Partial<Order> = {}): Order => ({
id: 'order-1',
userId: 'user-test-01',
items: [buildCartItem()],
subtotal: 25,
status: 'pending',
...overrides,
});

// Fetch mock helper — used in StorePage component tests
export function mockFetchOnce(body: unknown, status = 200) {
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: status < 300,
status,
json: async () => body,
});
}

// Usage examples:
// const shirt = buildCartItem({ quantity: 3 });
// const confirmed = buildOrder({ status: 'confirmed', subtotal: 90 });
// mockFetchOnce({ data: { items: [buildCartItem()] } });
✅ What makes these factories good:

Defaults are obviously fake ("Workshop T-Shirt", "user-test-01") — not real-looking PII

  • Spread overrides let each test express only what it cares about
  • mockFetchOnce returns a proper Response-like object including ok, status, and json()

Part B — Mock Service Stubs​

Ask Copilot to generate a complete mock for a service interface. This is especially useful for dependency-heavy classes:

Generate a Jest mock factory for NotificationService with methods:
sendOrderConfirmation(orderId: string, email: string): Promise<void>
sendShippingUpdate(orderId: string, trackingUrl: string): Promise<void>
All methods should be jest.fn() returning resolved promises.
// tests/mocks/mockNotificationService.ts (Copilot-generated)
import { NotificationService } from '../../src/services/notificationService';

export const createMockNotificationService = (): jest.Mocked<NotificationService> => ({
sendOrderConfirmation: jest.fn().mockResolvedValue(undefined),
sendShippingUpdate: jest.fn().mockResolvedValue(undefined),
});

// Usage:
// const notifService = createMockNotificationService();
// notifService.sendOrderConfirmation.mockRejectedValueOnce(new Error('SMTP failure'));

Part C — Test Data at Scale with Faker​

For tests that need many records (pagination, bulk cart operations), ask Copilot to use a fake data library:

Generate a function that creates an array of N fake CartItem objects using @faker-js/faker.
Fields: id (uuid), productId (uuid), name (commerce.productName), price (number 1-500), quantity (integer 1-10).
Keep prices and quantities in realistic ranges for a checkout pipeline.
// tests/factories/bulkCartFactory.ts (Copilot-generated)
import { faker } from '@faker-js/faker';
import { CartItem } from '../../src/models/cart';

export const createCartItems = (count: number): CartItem[] =>
Array.from({ length: count }, () => ({
id: faker.string.uuid(),
productId: faker.string.uuid(),
name: faker.commerce.productName(),
price: faker.number.float({ min: 1, max: 500, fractionDigits: 2 }),
quantity: faker.number.int({ min: 1, max: 10 }),
}));

Security Guardrail — What to Check​

RiskExampleSafe Alternative
Hardcoded passwordspassword: 'Secret123!'password: 'test-password-placeholder'
Real-looking API keysapiKey: 'sk-abc123xyz...'apiKey: 'test-api-key'
Real email domainsemail: 'alice@company.com'email: 'alice@example.com' (RFC 2606 reserved)
Real-looking PIIssn: '123-45-6789'Use faker.string.alphanumeric(9)
JWT tokensFull valid signed JWT in a fixtureMock the auth middleware; use 'test-token'
🔒 Add to your .gitleaks.toml

Configure secret scanning to catch AI-generated test data that looks like credentials before it reaches your repo. Copilot sometimes generates syntactically valid tokens as examples.

Step — Run a Test Using the Factory​

// tests/unit/calculateDiscount.test.ts — using checkout factories
import { buildCartItem, buildOrder } from '../factories';

it('FLAT5 is applied to a confirmed order above the minimum', () => {
const order = buildOrder({ subtotal: 50, status: 'confirmed' });
const result = calculateDiscount({ subtotal: order.subtotal, code: 'FLAT5' });

expect(result.discountAmount).toBe(5);
expect(result.finalTotal).toBe(45);
});

it('StorePage renders the cart item name after Add', async () => {
mockFetchOnce({ data: { items: [buildCartItem({ name: 'Workshop T-Shirt' })] } });
// ... render StorePage and assert
});
Expected output
✓ FLAT5 is applied to a confirmed order above the minimum (2 ms)
🏋️Hands-on Challenge
⏱6 min

The buildOrder factory above does not include adiscount field. Ask Copilot to extend the factory, then review the output for the three most common problems: hardcoded IDs, real-looking amounts, and missing override support.

HintPrompt template
Extend the buildOrder factory to include an optional discount field:
{ code: string; discountAmount: number; finalTotal: number }
Make it optional (undefined by default).
Do not use real-looking amounts — keep values obviously fake (e.g. 5, 10, 25).
BonusShared factory index

Create a tests/factories/index.ts that re-exports all factories. Ask Copilot to generate the barrel file once you have two or more factories.

Key Takeaways​

  • Factories with Partial&lt;T&gt; overrides eliminate per-test boilerplate
  • Copilot generates good factory stubs from TypeScript types — always review for PII risk
  • Never commit real-looking credentials, tokens, or PII — even in test files
  • Use example.com (RFC 2606 reserved) for fake email domains
  • Configure gitleaks or truffleHog in your CI pipeline to catch secret leaks
Done with this step? Mark it to track your progress.