Mystery meat test data is a recipe for confusion. When your test fails six months from now, you should understand the test data instantly, not need archaeology skills.
// 😱 BAD: Shared, mutable test data // fixtures/users.json { "testUser": { "id": 1, "email": "test@example.com", "balance": 100 } } // test1.js test('user can make purchase', () => { const user = loadFixture('users.json').testUser; makePurchase(user, 50); expect(user.balance).toBe(50); // Mutates shared data! }); // test2.js test('user can receive refund', () => { const user = loadFixture('users.json').testUser; // Fails because test1 mutated the balance! expect(user.balance).toBe(100); refund(user, 25); expect(user.balance).toBe(125); });
// 😱 BAD: What do these values mean? test('calculates shipping cost', () => { const order = createOrder( 3, // What is 3? 'US', // Shipping to or from US? true, // What is true? false, // What is false? 2 // What is 2? ); expect(calculateShipping(order)).toBe(15.99); // Why 15.99? });
// 😱 BAD: Hidden data relationships test('applies bulk discount', () => { // Depends on products.json having items with specific IDs const cart = new Cart(); cart.addItem(101, 5); // Assumes product 101 exists cart.addItem(102, 3); // Assumes product 102 exists // Assumes prices in fixture: 101 = $10, 102 = $20 expect(cart.total()).toBe(110); // How did we get 110? });
// ✅ GOOD: Centralized test data creation class UserMother { static simple() { return { id: generateId(), email: 'user@example.com', name: 'Test User', balance: 0 }; } static withBalance(amount) { return { ...this.simple(), balance: amount }; } static premium() { return { ...this.simple(), email: 'premium@example.com', name: 'Premium User', subscription: 'premium', balance: 1000 }; } static suspended() { return { ...this.simple(), status: 'suspended', suspendedAt: new Date() }; } } // Usage test('premium users get free shipping', () => { const user = UserMother.premium(); const shipping = calculateShipping(user, standardOrder); expect(shipping).toBe(0); });
// ✅ GOOD: Flexible, explicit test data class OrderBuilder { constructor() { this.order = { id: generateId(), items: [], shipping: 'standard', country: 'US', express: false, giftWrap: false, discount: null }; } withItems(...items) { this.order.items = items; return this; } withExpressShipping() { this.order.shipping = 'express'; this.order.express = true; return this; } toCountry(country) { this.order.country = country; return this; } withDiscount(code, percentage) { this.order.discount = { code, percentage }; return this; } build() { return { ...this.order }; } } // Usage - Explicit and readable! test('calculates international express shipping', () => { const order = new OrderBuilder() .withItems( { name: 'Widget', price: 29.99, quantity: 2 }, { name: 'Gadget', price: 49.99, quantity: 1 } ) .toCountry('UK') .withExpressShipping() .build(); const shipping = calculateShipping(order); expect(shipping).toBe(35.00); // Clear what we're testing });
// ✅ GOOD: Simple factories with defaults function createUser(overrides = {}) { return { id: uniqueId(), email: `test-${Date.now()}@example.com`, name: 'Test User', createdAt: new Date(), verified: true, ...overrides // Explicit overrides last }; } function createProduct(overrides = {}) { return { id: uniqueId(), name: 'Test Product', price: 10.00, stock: 100, category: 'general', ...overrides }; } // Usage - minimal and clear test('out of stock products cannot be purchased', () => { const product = createProduct({ stock: 0 }); const result = purchaseProduct(product, 1); expect(result.success).toBe(false); expect(result.error).toBe('Product out of stock'); });
// ❌ BAD: Hidden important details const user = getTestUser(); expect(canPurchaseAlcohol(user)).toBe(true); // ✅ GOOD: Relevant data is visible const user = createUser({ age: 21 }); expect(canPurchaseAlcohol(user)).toBe(true);
// ❌ BAD: Shared array modified by tests const sharedItems = [item1, item2, item3]; // ✅ GOOD: Fresh array for each test function getTestItems() { return [ createItem({ name: 'Item 1' }), createItem({ name: 'Item 2' }), createItem({ name: 'Item 3' }) ]; }
// ❌ BAD: Same email in every test email: 'test@example.com' // ✅ GOOD: Unique but recognizable email: `test-${testName}-${Date.now()}@example.com` email: `user-${uuid()}@test.local`
// ❌ BAD: Everything including kitchen sink const user = { id: 1, email: '...', name: '...', address: {...}, preferences: {...}, history: [...], social: {...} // 20 more fields... }; // ✅ GOOD: Just what we need const user = { email: 'test@example.com', subscriptionStatus: 'active' };
Use random data when:
Use deterministic data when: