tests.md
2.2 KB查看排版效果| 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 | |
| 63 | **Tautological tests**: Expected value restates the implementation, so the test passes by construction. |
| 64 | |
| 65 | ```typescript |
| 66 | // BAD: Expected value is recomputed the way the code computes it |
| 67 | test("calculateTotal sums line items", () => { |
| 68 | const items = [{ price: 10 }, { price: 5 }]; |
| 69 | const expected = items.reduce((sum, i) => sum + i.price, 0); |
| 70 | expect(calculateTotal(items)).toBe(expected); |
| 71 | }); |
| 72 | |
| 73 | // GOOD: Expected value is an independent, known literal |
| 74 | test("calculateTotal sums line items", () => { |
| 75 | expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15); |
| 76 | }); |
| 77 | ``` |
文件内容是平台检查时读出的文本,只供查看。平台不会运行包里的任何脚本或程序。

