1 min read
Good and Bad Tests
Good tests
Integration-style: test through real interfaces, not mocks of internal parts.
// GOOD: Tests observable behaviour
test("user can checkout with valid cart", async () => {
const cart = createCart()
cart.add(product)
const result = await checkout(cart, paymentMethod)
expect(result.status).toBe("confirmed")
})
Characteristics:
- Tests behaviour users/callers care about
- Uses public API only
- Survives internal refactors
- Describes WHAT, not HOW
- One logical assertion per test
Bad tests
Implementation-detail tests: coupled to internal structure.
// BAD: Tests implementation details
test("checkout calls paymentService.process", async () => {
const mockPayment = mock(paymentService)
await checkout(cart, payment)
expect(mockPayment.process).toHaveBeenCalledWith(cart.total)
})
Red flags:
- Mocking internal collaborators
- Testing private methods
- Asserting on call counts/order
- Test breaks when refactoring without behaviour change
- Test name describes HOW not WHAT
- Verifying through external means instead of the interface
// BAD: Bypasses interface to verify
test("createUser saves to database", async () => {
await createUser({name: "Alice"})
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"])
expect(row).toBeDefined()
})
// GOOD: Verifies through interface
test("createUser makes user retrievable", async () => {
const user = await createUser({name: "Alice"})
const retrieved = await getUser(user.id)
expect(retrieved.name).toBe("Alice")
})
Tautological tests: expected value restates the implementation, so the test passes by construction.
// BAD: Expected value is recomputed the way the code computes it
test("calculateTotal sums line items", () => {
const items = [{price: 10}, {price: 5}]
const expected = items.reduce((sum, i) => sum + i.price, 0)
expect(calculateTotal(items)).toBe(expected)
})
// GOOD: Expected value is an independent, known literal
test("calculateTotal sums line items", () => {
expect(calculateTotal([{price: 10}, {price: 5}])).toBe(15)
})
Last updated Oct 08, 2026