A failing test at 3 AM should tell you what's wrong without requiring a debugging session. "Expected true but got false" is not a helpful message.
// 😱 BAD: What failed? What was it checking?
test('user validation', () => {
const result = validateUser(userData);
expect(result).toBe(true);
});
// Failure output:
// ✗ user validation
// Expected: true
// Received: false
// 😱 BAD: Which field failed validation?
test('form validation', () => {
const form = { email: 'bad', age: 'seventeen', name: '' };
const errors = validateForm(form);
expect(errors.length).toBe(0);
});
// Failure output:
// ✗ form validation
// Expected: 0
// Received: 3// 😱 BAD: What do these numbers mean?
test('calculates total', () => {
const result = calculateTotal(order);
expect(result).toBe(157.45);
});
// Failure output:
// ✗ calculates total
// Expected: 157.45
// Received: 162.38
// What changed? Tax? Shipping? Discount? Item prices?// ✅ GOOD: Clear context in failure message
test('user validation', () => {
const result = validateUser(userData);
expect(result.valid).toBe(true,
`User validation failed: ${result.errors.join(', ')}`
);
});
// Failure output:
// ✗ user validation
// User validation failed: email format invalid, age must be number
// ✅ GOOD: Show what was being validated
test('form validation', () => {
const form = { email: 'bad', age: 'seventeen', name: '' };
const errors = validateForm(form);
expect(errors).toEqual([],
`Form validation failed:
Input: ${JSON.stringify(form, null, 2)}
Errors: ${JSON.stringify(errors, null, 2)}`
);
});// ✅ GOOD: Break down the calculation for clarity
test('calculates order total correctly', () => {
const order = createOrder({
items: [
{ name: 'Widget', price: 50, quantity: 2 },
{ name: 'Gadget', price: 30, quantity: 1 }
],
discount: 'SAVE10',
shipping: 'express'
});
const result = calculateTotal(order);
// Break down the expectation
const expectedSubtotal = 130; // (50*2 + 30*1)
const expectedDiscount = 13; // 10% off
const expectedShipping = 15; // express shipping
const expectedTax = 14.70; // 12% tax on (130-13)
const expectedTotal = expectedSubtotal - expectedDiscount + expectedShipping + expectedTax;
expect(result).toEqual({
subtotal: expectedSubtotal,
discount: expectedDiscount,
shipping: expectedShipping,
tax: expectedTax,
total: expectedTotal
}, `
Order total calculation mismatch:
Items: 2x Widget @ $50, 1x Gadget @ $30
Expected breakdown:
Subtotal: $${expectedSubtotal}
Discount (SAVE10): -$${expectedDiscount}
Shipping (express): +$${expectedShipping}
Tax (12%): +$${expectedTax}
Total: $${expectedTotal}
Actual: $${result.total}
`);
});// Provide context about what was being tested
function assertUserCanPurchase(user, product) {
const canPurchase = checkPurchaseEligibility(user, product);
expect(canPurchase).toBe(true, `
User ${user.email} cannot purchase ${product.name}:
- User age: ${user.age} (required: ${product.minAge})
- User balance: $${user.balance} (price: $${product.price})
- User verified: ${user.verified}
- Product available: ${product.inStock}
`);
}
test('verified adult can purchase age-restricted item', () => {
const user = createUser({ age: 21, balance: 100, verified: true });
const product = createProduct({ name: 'Wine', price: 25, minAge: 21 });
assertUserCanPurchase(user, product);
});// Create domain-specific matchers with good messages
expect.extend({
toBeValidEmail(received) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const pass = emailRegex.test(received);
return {
pass,
message: () => pass
? `Expected "${received}" not to be a valid email`
: `Expected "${received}" to be a valid email address.
Common issues: missing @, no domain, spaces`
};
},
toBeWithinRange(received, min, max) {
const pass = received >= min && received <= max;
return {
pass,
message: () => pass
? `Expected ${received} not to be within range [${min}, ${max}]`
: `Expected ${received} to be within range [${min}, ${max}]
Value is ${received < min ? 'too low' : 'too high'} by ${
Math.abs(received - (received < min ? min : max))
}`
};
}
});
// Usage
test('validates email format', () => {
expect('not-an-email').toBeValidEmail();
// Failure: Expected "not-an-email" to be a valid email address.
// Common issues: missing @, no domain, spaces
});
test('age within valid range', () => {
expect(150).toBeWithinRange(18, 120);
// Failure: Expected 150 to be within range [18, 120]
// Value is too high by 30
});// Add context to snapshot tests
test('renders error state correctly', () => {
const errors = [
'Email is required',
'Password too weak',
'Terms must be accepted'
];
const component = render(<Form errors={errors} />);
expect(component).toMatchSnapshot(`
Form with validation errors:
- ${errors.length} errors shown
- Fields: email, password, terms
- State: initial submission attempt
`);
});// Show both sides of the equation
expect(result).toBe(expected, `
Input: ${JSON.stringify(input)}
Expected: ${expected}
Actual: ${result}
`);expect(user.canVote).toBe(true, `
User cannot vote:
Age: ${user.age} (must be >= 18)
Citizenship: ${user.citizenship} (must be valid)
Registration: ${user.registered} (must be registered)
`);// Instead of multiple assertions
expect(user.name).toBe('Alice');
expect(user.age).toBe(25);
expect(user.role).toBe('admin');
// Use object comparison to see all issues
expect(user).toEqual({
name: 'Alice',
age: 25,
role: 'admin'
});await expect(fetchUser(id))
.rejects
.toThrow(`Failed to fetch user ${id}: Network timeout after 5s`);
await expect(async () => {
const user = await fetchUser(id);
return user.status;
}).rejects.toThrow(`User ${id} is inactive`);