| 1 | # Good and Bad Tests |
| 2 | |
| 3 | ## Good Tests |
| 4 | |
| 5 | **Integration-style**: Test through real interfaces, not mocks of internal parts. |
| 6 | |
| 7 | ```typescript |
| 8 | // GOOD: Tests observable behavior |
| 9 | test("user can checkout with valid cart", async () => { |
| 10 | const cart = createCart(); |
| 11 | cart.add(product); |
| 12 | const result = await checkout(cart, paymentMethod); |
| 13 | expect(result.status).toBe("confirmed"); |
| 14 | }); |
| 15 | ``` |
| 16 | |
| 17 | Characteristics: |
| 18 | |
| 19 | - Tests behavior users/callers care about |
| 20 | - Uses public API only |
| 21 | - Survives internal refactors |
| 22 | - Describes WHAT, not HOW |
| 23 | - One logical assertion per test |
| 24 | |
| 25 | ## Bad Tests |
| 26 | |
| 27 | **Implementation-detail tests**: Coupled to internal structure. |
| 28 | |
| 29 | ```typescript |
| 30 | // BAD: Tests implementation details |
| 31 | test("checkout calls paymentService.process", async () => { |
| 32 | const mockPayment = jest.mock(paymentService); |
| 33 | await checkout(cart, payment); |
| 34 | expect(mockPayment.process).toHaveBeenCalledWith(cart.total); |
| 35 | }); |
| 36 | ``` |
| 37 | |
| 38 | Red flags: |
| 39 | |
| 40 | - Mocking internal collaborators |
| 41 | - Testing private methods |
| 42 | - Asserting on call counts/order |
| 43 | - Test breaks when refactoring without behavior change |
| 44 | - Test name describes HOW not WHAT |
| 45 | - Verifying through external means instead of interface |
| 46 | |
| 47 | ```typescript |
| 48 | // BAD: Bypasses interface to verify |
| 49 | test("createUser saves to database", async () => { |
| 50 | await createUser({ name: "Alice" }); |
| 51 | const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]); |
| 52 | expect(row).toBeDefined(); |
| 53 | }); |
| 54 | |
| 55 | // GOOD: Verifies through interface |
| 56 | test("createUser makes user retrievable", async () => { |
| 57 | const user = await createUser({ name: "Alice" }); |
| 58 | const retrieved = await getUser(user.id); |
| 59 | expect(retrieved.name).toBe("Alice"); |
| 60 | }); |
| 61 | ``` |
| 62 |