Prompt Claude Code để tạo test cho các trường hợp biên
Tìm hiểu prompt tạo test cho Claude Code giúp sinh các unit test có "tín hiệu cao" bằng cách tập trung vào ranh giới, bất biến và chế độ lỗi thay vì chỉ các trường hợp bình thường.

Tại sao tạo test chỉ theo happy-path lãng phí thời gian
Các bộ test tự sinh thường trông ấn tượng: hàng chục test, nhiều mã thiết lập, và tên hàm nào cũng xuất hiện. Nhưng nhiều test trong số đó chỉ là kiểm tra “nó hoạt động khi mọi thứ bình thường”. Chúng dễ qua, hiếm khi bắt được lỗi, và vẫn tốn thời gian để đọc và bảo trì.
Với một prompt tạo test Claude Code thông thường, mô hình có xu hướng phản chiếu các ví dụ đầu vào nó thấy. Bạn nhận được các biến thể trông khác nhau nhưng kiểm thử cùng một hành vi. Kết quả là một bộ test lớn nhưng che phủ mỏng ở những chỗ quan trọng.
Test có tín hiệu cao thì khác. Đó là tập nhỏ các test có thể đã bắt được sự cố tháng trước. Chúng sẽ fail khi hành vi thay đổi theo cách rủi ro, và vẫn ổn định khi refactor vô hại xảy ra. Một test có tín hiệu cao có thể giá trị bằng hai mươi kiểm tra “trả về giá trị mong đợi”.
Tạo test theo happy-path có vài triệu chứng rõ rệt:
- Nhiều test chỉ khác nhau ở nhãn đầu vào, chứ không phải ở thứ có thể hỏng.
- Các assert nông ("không null", "status là 200") thay vì kiểm tra ý nghĩa.
- Thiết lập nặng hơn hành vi được kiểm thử, khiến người ta bỏ cập nhật test.
- Coverage trông cao nhưng các edge case bị bỏ qua.
Hãy tưởng tượng một hàm áp dụng mã giảm giá. Test happy-path xác nhận rằng “SAVE10” giảm giá. Lỗi thực tế ẩn ở chỗ khác: giá bằng 0 hoặc âm, mã hết hạn, các cạnh làm tròn, hoặc giới hạn giảm giá tối đa. Đó là những trường hợp gây ra tổng tiền sai, khách hàng giận dữ, và rollback lúc nửa đêm.
Mục tiêu là chuyển từ “nhiều test hơn” sang “test tốt hơn” bằng cách nhắm vào ba mục tiêu: ranh giới, chế độ lỗi và bất biến.
Ba mục tiêu: ranh giới, chế độ lỗi, bất biến
Nếu bạn muốn unit test có tín hiệu cao, hãy ngừng yêu cầu “nhiều test hơn” và bắt đầu yêu cầu ba loại cụ thể này. Đây là cốt lõi của một prompt tạo test Claude Code mà tạo ra coverage hữu dụng thay vì một đống kiểm tra "hoạt động trên input bình thường".
1) Ranh giới (nơi lỗi ẩn)
Ranh giới là mép của những gì mã chấp nhận hoặc sinh ra. Nhiều lỗi thực tế là off-by-one, trạng thái rỗng, hoặc timeout mà không bao giờ xuất hiện trong happy path.
Nghĩ theo tối thiểu và tối đa (0, 1, độ dài tối đa), rỗng vs có ("", [], nil), off-by-one (n-1, n, n+1), và giới hạn thời gian (gần ngưỡng cắt).
Ví dụ: nếu API chấp nhận “tối đa 100 mục”, hãy test 100 và 101, đừng chỉ test 3.
2) Chế độ lỗi (chứng minh nó fail an toàn)
Chế độ lỗi là những cách hệ thống có thể vỡ: input sai, phụ thuộc mất, kết quả một phần, hoặc lỗi từ upstream. Test chế độ lỗi tốt kiểm tra hành vi dưới áp lực, không chỉ output trong điều kiện lý tưởng.
Ví dụ: khi gọi DB thất bại, hàm có trả về lỗi rõ ràng và tránh ghi dữ liệu một phần không?
3) Bất biến (luật phải luôn đúng)
Bất biến là những sự thật luôn phải đúng trước và sau một cuộc gọi. Chúng biến tính đúng đắn mơ hồ thành các assert rõ ràng.
Ví dụ:
- “Số dư không bao giờ âm” sau bất kỳ lần rút tiền nào.
- “ID là duy nhất” ngay cả khi tạo mục nhanh.
- “Khi lỗi, không có thay đổi trạng thái” (không hàng mới, không cờ bị lật).
Khi bạn tập trung vào ba mục này, bạn có ít test hơn, nhưng mỗi test mang nhiều tín hiệu hơn.
Chuẩn bị: trích xuất một hợp đồng nhỏ trước khi viết test
Nếu bạn yêu cầu test quá sớm, bạn thường nhận được một đống kiểm tra lịch sự “hoạt động như mong đợi”. Cách khắc phục đơn giản là viết một hợp đồng nhỏ trước, rồi sinh test từ hợp đồng đó. Đây là cách nhanh nhất để biến prompt tạo test Claude Code thành thứ tìm được lỗi thật.
Một hợp đồng hữu ích đủ ngắn để đọc trong một hơi. Hướng tới 5–10 dòng trả lời ba câu hỏi: vào gì, ra gì, và gì khác thay đổi.
Mẫu hợp đồng 5–10 dòng
Viết hợp đồng bằng ngôn ngữ thường, không phải code, và chỉ bao gồm những gì bạn có thể kiểm thử.
- Inputs: kiểu, phạm vi cho phép, và điều gì tính là “rỗng” hoặc “không có”.
- Output: giá trị trả về hoặc dạng lỗi, và điều gì đảm bảo khi “thành công”.
- Side effects: thay đổi trạng thái, hàng DB, cuộc gọi mạng, files, logs.
- Assumptions: những điều caller thường sai (timezone, encoding, auth, ordering).
- “Không được xảy ra”: crash, mất dữ liệu im lặng, trừ tiền hai lần, ghi một phần.
Khi bạn có điều đó, quét để tìm nơi thực tế có thể phá vỡ giả định. Những chỗ đó trở thành ranh giới (min/max, zero, overflow, chuỗi rỗng, trùng lặp) và chế độ lỗi (timeout, permission denied, vi phạm ràng buộc unique, input bị hỏng).
Ví dụ cụ thể cho một tính năng như reserveInventory(itemId, qty):
Hợp đồng có thể nói qty phải là số nguyên dương, hàm phải là nguyên tử, và không bao giờ tạo tồn kho âm. Điều đó ngay lập tức gợi ý test có tín hiệu cao: qty = 0, qty = 1, qty lớn hơn tồn có sẵn, gọi đồng thời, và một lỗi DB ép dừng giữa chừng.
Nếu bạn dùng công cụ vibe-coding như Koder.ai, workflow tương tự áp dụng: viết hợp đồng trong chat trước, rồi sinh test tấn công trực tiếp ranh giới, chế độ lỗi, và danh sách “không được xảy ra”.
Mẫu prompt: bản thiết kế test có tín hiệu cao
Dùng prompt Claude Code này khi bạn muốn ít test hơn nhưng mỗi test đều có tác dụng. Bước quan trọng là ép phải có kế hoạch test trước, rồi chỉ sinh code test sau khi bạn duyệt kế hoạch.
You are helping me write HIGH-SIGNAL unit tests.
Context
- Language/framework: <fill in>
- Function/module under test: <name + short description>
- Inputs: <types, ranges, constraints>
- Outputs: <types + meaning>
- Side effects/external calls: <db, network, clock, randomness>
Contract (keep it small)
1) Preconditions: <what must be true>
2) Postconditions: <what must be true after>
3) Error behavior: <how failures are surfaced>
Task
PHASE 1 (plan only, no code):
A) Propose 6-10 tests max. Do not include “happy path” unless it protects an invariant.
B) For each test, state: intent, setup, input, expected result, and WHY it is high-signal.
C) Invariants: list 3-5 invariants and how each will be asserted.
D) Boundary matrix: propose a small matrix of boundary values (min/max/empty/null/off-by-one/too-long/invalid enum).
E) Failure modes: list negative tests that prove safe behavior (no crash, no partial write, clear error).
Stop after PHASE 1 and ask for approval.
PHASE 2 (after approval):
Generate the actual test code with clear names and minimal mocks.
Một mẹo thực tế là yêu cầu boundary matrix ở dạng bảng gọn để các khoảng hổng hiển nhiên:
| Dimension | Valid edge | Just outside | “Weird” value | Expected behavior |
|---|---|---|---|---|
| length | 0 | -1 | 10,000 | error vs clamp vs accept |
Nếu Claude đề xuất 20 test, hãy phản hồi. Yêu cầu hợp nhất các trường hợp giống nhau và chỉ giữ những test có thể bắt lỗi thực sự (off-by-one, sai kiểu lỗi, mất dữ liệu im lặng, bất biến bị phá).
Từng bước: chạy prompt và biến output thành test
Bắt đầu với một hợp đồng nhỏ, cụ thể cho hành vi bạn muốn. Dán chữ ký hàm, mô tả ngắn về inputs và outputs, và bất kỳ test hiện có (dù chỉ là happy-path). Điều này giữ mô hình bám sát vào điều mã thực sự làm, không phải đoán mò.
Tiếp theo, yêu cầu một bảng rủi ro trước khi yêu cầu bất kỳ code test nào. Yêu cầu ba cột: boundary cases (mép của input hợp lệ), failure modes (input sai, dữ liệu thiếu, timeout), và invariants (luật phải luôn đúng). Thêm một câu mỗi hàng: “tại sao điều này có thể hỏng.” Một bảng đơn giản lộ ra khoảng trống nhanh hơn một đống file test.
Sau đó chọn tập test nhỏ nhất mà mỗi test có mục đích bắt lỗi khác nhau. Nếu hai test fail vì cùng một lý do, giữ test mạnh hơn.
Một quy tắc chọn thực tế:
- Giữ test chạm các ranh giới khác nhau (min, max, rỗng, off-by-one).
- Giữ test chứng minh hành vi an toàn khi phụ thuộc lỗi (lỗi rõ ràng, không ghi một phần, không crash).
- Giữ test khẳng định một bất biến (thứ tự, tổng, idempotency, không trùng).
- Cắt test chỉ lặp lại “hoạt động với input bình thường”.
Cuối cùng, yêu cầu một giải thích ngắn cho mỗi test: test sẽ bắt lỗi gì nếu nó fail. Nếu giải thích mơ hồ (“xác nhận hành vi”), test có lẽ là tín hiệu thấp.
Cách mã hóa bất biến vào các assert
Bất biến là luật phải luôn đúng bất kể input hợp lệ nào. Với kiểm thử dựa trên bất biến, bạn viết quy tắc bằng ngôn ngữ thường trước, rồi biến nó thành assert có thể fail rõ ràng.
Chọn 1–2 bất biến thực sự bảo vệ bạn khỏi lỗi thật. Bất biến tốt thường về an toàn (không mất dữ liệu), nhất quán (cùng input → cùng output), hoặc giới hạn (không vượt quá cap).
Biến bất biến thành kiểm tra có thể chứng minh
Viết bất biến như một câu ngắn, rồi quyết định bằng chứng test có thể quan sát được: giá trị trả về, dữ liệu lưu, sự kiện phát ra, hoặc các cuộc gọi tới phụ thuộc. Assert mạnh kiểm tra cả kết quả và side effect, vì nhiều lỗi ẩn trong “trả về OK nhưng ghi sai”.
Ví dụ, hàm áp mã giảm giá vào đơn hàng:
- Bất biến: tổng cuối cùng không bao giờ âm.
- Bất biến: áp cùng mã hai lần không giảm hai lần.
Giờ mã hóa thành assert quan sát được:
expect(result.total).toBeGreaterThanOrEqual(0)
expect(db.getOrder(orderId).discountCents).toBe(originalDiscountCents)
Tránh assert mơ hồ như “trả về kết quả mong đợi”. Assert quy tắc cụ thể (không âm), và side effect cụ thể (giảm giá chỉ lưu một lần).
Thêm ghi chú counterexample để test không bị mòn
Với mỗi bất biến, thêm một ghi chú ngắn trong test về dữ liệu sẽ vi phạm nó. Điều này giữ test khỏi drift thành kiểm tra happy-path sau này.
Một mẫu đơn giản bền theo thời gian:
- Đặt bất biến trong tên test.
- Assert bất biến trên output.
- Assert side effect chính (hoặc không có side effect).
- Thêm một comment mô tả một trường hợp vi phạm (ví dụ: mã giảm giá rất lớn hoặc áp trùng lặp).
Chế độ lỗi: viết test chứng minh hành vi an toàn
Test có tín hiệu cao thường là những test xác nhận mã của bạn fail an toàn. Nếu mô hình chỉ sinh test happy-path, bạn hầu như không biết feature xử lý thế nào khi input và phụ thuộc xấu.
Bắt đầu bằng việc quyết định “an toàn” nghĩa là gì với feature này. Nó trả về lỗi kiểu có cấu trúc? Nó fallback về giá trị mặc định? Nó thử lại một lần rồi dừng? Viết hành vi mong đợi đó ra trong một câu, rồi buộc test chứng minh nó.
Khi yêu cầu Claude Code sinh test chế độ lỗi, giữ mục tiêu chặt: phủ các cách hệ thống có thể hỏng, và assert phản ứng chính xác bạn muốn. Một câu hữu ích: “Ưu tiên ít test hơn với assert mạnh hơn thay vì nhiều test nông.”
Các nhóm lỗi giúp tạo test tốt:
- Input xấu: định dạng sai, trường bắt buộc thiếu, giá trị ngoài khoảng
- Phụ thuộc lỗi: timeout, 500, response rỗng, payload bị hỏng
- Vấn đề thứ tự: sự kiện đến sai thứ tự, trùng, ghi một phần
- Đồng thời: cập nhật tranh chấp, kiểm tra idempotency
- Hồi phục: khi trả lỗi vs fallback vs retry
Ví dụ: endpoint tạo user và gọi service email để gửi thư chào mừng. Test low-value kiểm tra “trả 201”. Test high-signal kiểm tra rằng nếu service email timeout, bạn hoặc (a) vẫn tạo user và trả 201 với flag “email_pending”, hoặc (b) trả 503 rõ ràng và không tạo user. Chọn một hành vi, rồi assert cả response lẫn side effect.
Cũng test việc không rò rỉ. Nếu validation fail, đảm bảo không có gì được ghi vào DB. Nếu phụ thuộc trả payload bị hỏng, đảm bảo bạn không ném exception chưa xử lý hoặc trả stack trace thô.
Bẫy phổ biến tạo ra test tín hiệu thấp
Bộ test tín hiệu thấp thường xuất hiện khi mô hình được thưởng vì số lượng. Nếu prompt Claude Code của bạn yêu cầu “20 unit test”, bạn thường nhận được các biến thể nhỏ trông có vẻ đầy đủ nhưng không bắt gì thêm.
Bẫy phổ biến:
- Test trông giống nhau: cùng một input hợp lệ lặp lại với các chuỗi hoặc số khác nhau.
- Test phản chiếu code: assert các bước private hoặc helper thay vì hành vi quan sát được.
- Mock gần như mọi thứ: thay DB, clock, network, config cùng lúc.
- Assert yếu: chỉ kiểm tra “không lỗi”, “không null”, hoặc “status 200”.
- Trạng thái chia sẻ bẩn: để lại dữ liệu seed, biến global thay đổi, hoặc cache.
Ví dụ: hàm “create user”. Mười test happy-path có thể thay email và vẫn bỏ qua điều quan trọng: từ chối email trùng lặp, xử lý password rỗng, và đảm bảo ID trả về là duy nhất và ổn định.
Các rào chắn giúp review:
- Yêu cầu mỗi test nêu rủi ro nó che phủ (boundary, failure mode, hoặc invariant).
- Tránh kiểm tra chỉ dựa vào implementation trừ khi chúng thay đổi hành vi quan sát được.
- Giữ mocking ở mức tối thiểu, và cho phép vài test nhỏ chạm điểm tích hợp thật khi khả thi.
- Yêu cầu assert mạnh: output chính xác, thay đổi trạng thái, và loại/lời nhắn lỗi.
- Thêm quy tắc dọn dẹp để test không phụ thuộc thứ tự.
Ví dụ: biến một feature thành một tập test nhỏ, mạnh
Giả sử feature: áp mã giảm giá khi checkout.
Hợp đồng (nhỏ và testable): cho subtotal của giỏ tính bằng cents và một coupon tùy chọn, trả về tổng cuối cùng bằng cents. Luật: coupon phần trăm làm tròn xuống cent gần nhất, coupon cố định trừ một khoản cố định, và tổng không bao giờ xuống dưới 0. Coupon có thể không hợp lệ, hết hạn, hoặc đã dùng.
Đừng hỏi “tests cho applyCoupon()”. Hãy yêu cầu kiểm thử ranh giới, chế độ lỗi, và bất biến gắn với hợp đồng này.
Ranh giới để ép hành vi biên
Chọn inputs dễ làm hỏng phép toán hoặc validation: chuỗi coupon rỗng, subtotal = 0, subtotal ngay dưới và trên ngưỡng chi tiêu tối thiểu, fixed discount lớn hơn subtotal, và phần trăm như 33% gây làm tròn.
Chế độ lỗi để chứng minh hành vi an toàn
Giả sử lookup coupon có thể fail và trạng thái có thể sai: service coupon down, coupon hết hạn, hoặc coupon đã được redeem bởi user này. Test phải chứng minh điều gì xảy ra tiếp (coupon bị từ chối với lỗi rõ ràng, tổng không đổi).
Một tập test tối thiểu, có tín hiệu cao (5 test) và mục đích mỗi test:
- Từ chối mã rỗng hoặc chỉ khoảng trắng: bắt lỗi "chấp nhận rỗng" và trim sai.
- Coupon phần trăm làm tròn (subtotal 101, 33%): bắt lỗi làm tròn và off-by-one cent.
- Discount cố định lớn hơn subtotal (subtotal 500, discount 1000): chứng minh bất biến tổng không âm.
- Ngưỡng chi tiêu tối thiểu (subtotal 999 vs 1000): bắt logic so sánh sai (< vs <=).
- Lookup coupon lỗi hoặc timeout: chứng minh fallback an toàn (không áp discount) và xử lý lỗi ổn định.
Nếu các test này pass, bạn đã che gần hết các điểm dễ vỡ mà không nhồi bộ test bằng các happy-path giống nhau.
Checklist nhanh cho test AI-generated có tín hiệu cao
Trước khi chấp nhận output của mô hình, kiểm tra nhanh. Mục tiêu là test mỗi cái bảo vệ bạn khỏi một lỗi cụ thể, có khả năng xảy ra.
Dùng checklist này làm rào:
- Ranh giới theo input: với mỗi trường input (chuỗi, ID, timestamp, flag), bao gồm ít nhất một edge case (rỗng vs chỉ khoảng trắng, độ dài tối đa, zero vs âm, trường optional thiếu, một giá trị vượt giới).
- Phụ thuộc lỗi: bao ít nhất một test phụ thuộc misbehave (DB timeout, API 500, token hết hạn). Chứng minh hành vi an toàn (lỗi rõ ràng, không ghi một phần).
- Bất biến với assert mạnh: chọn 1–3 quy tắc phải luôn đúng và assert chúng trực tiếp. Tránh assert mơ hồ như “response ok”.
- Một lỗi duy nhất mỗi test: đọc tên test và hỏi, “Test này sẽ bắt lỗi gì?” Nếu hai test trả lời cùng câu, gộp lại.
- Test xóa: thử xóa một test. Nếu không mất ranh giới, chế độ lỗi, hay bất biến nào thì test đó không cần.
Mẹo sau khi sinh: đổi tên test theo mẫu “should <hành vi> when <điều kiện biên>” và “should not <kết quả xấu> when <sai lầm>”. Nếu không đổi tên gọn, test không đủ tập trung.
Nếu bạn xây với Koder.ai, checklist này cũng phù hợp với snapshot và rollback: sinh test, chạy chúng, và rollback nếu bộ test mới chỉ tạo nhiễu mà không cải thiện coverage.
Bước tiếp theo: biến điều này thành workflow lặp lại được
Xem prompt như một khung tái sử dụng, không phải yêu cầu một lần. Lưu một blueprint prompt (một prompt ép ranh giới, chế độ lỗi, và bất biến) và dùng lại cho mọi hàm, endpoint, hay flow UI mới.
Một thói quen đơn giản nâng kết quả nhanh: yêu cầu một câu cho mỗi test giải thích lỗi nó sẽ bắt. Nếu câu đó chung chung, test có lẽ là nhiễu.
Giữ một danh sách sống động các bất biến miền cho sản phẩm. Đừng để trong đầu. Thêm vào khi bạn tìm ra lỗi thật.
Một workflow nhẹ bạn có thể lặp:
- Trích một hợp đồng nhỏ: inputs, outputs, error handling, và 3–5 bất biến.
- Chạy prompt blueprint và yêu cầu ranh giới, chế độ lỗi, bất biến, kèm giải thích một dòng.
- Thực hiện chỉ 5–10 test hàng đầu che các rủi ro khác nhau.
- Refactor, rồi chạy lại prompt để thấy rủi ro mới xuất hiện.
- Lọc trùng và giữ các test đã từng bắt sự cố trước đây.
Nếu bạn xây app qua chat, chạy chu kỳ này trong Koder.ai (koder.ai) để hợp đồng, kế hoạch và test sinh ra cùng một chỗ. Khi refactor làm thay đổi hành vi bất ngờ, snapshot và rollback giúp so sánh và lặp đến khi bộ test tín hiệu cao ổn định.
Câu hỏi thường gặp
How many unit tests should I generate per function?
Default: aim for a small set that would catch a real bug.
A quick cap that works well is 6–10 tests per unit (function/module). If you need more, it usually means your unit is doing too much or your contract is unclear.
What’s wrong with generating lots of happy-path tests?
Happy-path tests mostly prove that your example still works. They tend to miss the stuff that breaks in production.
High-signal tests target:
- Boundaries (0/1/max, empty/null, off-by-one)
- Failure modes (timeouts, invalid inputs, dependency errors)
- Invariants (rules that must always hold, like “no partial write on error”)
What should I write down before asking an AI to generate tests?
Start with a tiny contract you can read in one breath:
- Inputs: types, allowed ranges, what counts as empty/missing
- Outputs: success shape and error shape
- Side effects: what can be written/changed (DB, files, network)
- “Must never happen”: crash, silent data loss, double charge, partial writes
Then generate tests from that contract, not from examples alone.
Which boundary cases are usually worth testing?
Test these first:
- Min/max values (0, 1, max, max+1)
- Empty vs present ("", [], null/nil)
- Off-by-one (n-1, n, n+1)
- Formatting edges (whitespace-only strings, leading zeros)
- Time cutoffs (just before/after expiry)
Pick one or two per input dimension so each test covers a unique risk.
How do I write a good “failure mode” test instead of a shallow one?
A good failure-mode test proves two things:
- The function returns a clear, expected error (type/message/status).
- It fails safely:
- no partial state changes
- no leaked internal details
- no retries or side effects you didn’t intend
If there’s a database write involved, always check what happened in storage after the failure.
How do I turn an invariant into a test assertion?
Default approach: turn the invariant into an assertion on observable outcomes.
Examples:
- “Total never negative” →
expect(total).toBeGreaterThanOrEqual(0) - “On error, no state changes” → assert no new rows / no flags flipped
- “Idempotent” → call twice and assert the second call doesn’t change state
Prefer checking both return value and side effects, because many bugs hide in “returned OK but wrote the wrong thing.”
When is a happy-path test still worth writing?
It’s worth keeping a happy-path test when it protects an invariant or a critical integration.
Good reasons to keep one:
- It asserts a key invariant on normal input (e.g., rounding rules)
- It locks down an API contract that callers rely on
- It guards against a past incident regression
Otherwise, trade it for boundary/failure tests that catch more classes of bugs.
What should I ask the model to output before generating test code?
Push for PHASE 1: plan only first.
Require the model to provide:
- 6–10 proposed tests max
- For each: intent, setup, input, expected result, why it’s high-signal
- A small boundary matrix
- A failure-mode list
- 3–5 invariants and how to assert them
Only after you approve the plan should it generate code. This prevents “20 look-alike tests” output.
How do I avoid tests that are brittle because they mock too much?
Default: mock only the boundary you don’t own (DB/network/clock), and keep everything else real.
To avoid over-mocking:
- Don’t mock internal helpers just to mirror implementation
- Use a real in-memory version when feasible, or a small fake with clear behavior
- Mock the clock/randomness only when it affects the assertion
If a test breaks on refactor but behavior didn’t change, it’s often over-mocked or too implementation-coupled.
How can I quickly tell if an AI-generated test is low-value?
Use a simple deletion test:
- If you delete the test and lose no boundary, no failure mode, and no invariant, it didn’t earn its place.
Also scan for duplicates:
- If two tests would fail for the same bug, keep the one with the stronger assertion.
- If assertions are just “not null” or “status 200,” strengthen them or remove the test.