Chapter 2 · Practice 5 of 5
Test boundaries and failure paths, not only the happy path
Why it matters
Most bugs hide at the edges: off by one errors, empty inputs, nulls, the exact limit and the value just past it. A suite that only tests the happy path will pass while production breaks.
For each function, go through this short list:
- Empty: empty string, empty list, zero, missing optional field
- Boundaries: the minimum, the maximum, one below and one above each
- Invalid: wrong type, malformed input, negative numbers where only positives make sense
- Failures: the dependency throws, times out or returns an error
Check that errors are specific. Asserting only that "something threw" passes even when the code throws for the wrong reason. Check the error type or message.
Example
it('rejects an empty cart', async () => {
await expect(checkout({ items: [] })).rejects.toThrow('Cart is empty');
});
it('accepts exactly the maximum quantity', () => {
expect(() => addItem(cart, { qty: 99 })).not.toThrow();
});
it('rejects one more than the maximum quantity', () => {
expect(() => addItem(cart, { qty: 100 })).toThrow(RangeError);
});