Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 5 — Sinh edge case và integration test với mocking

Đã đọc 0/9 mục · còn khoảng 4 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Sinh edge case và integration test với mocking

Unit test happy path thì dễ. Giá trị thật của agent nằm ở edge case (biên, lỗi, giá trị lạ) và integration test (nhiều thành phần phối hợp). Bài này dạy cách hướng dẫn agent phủ những vùng con người hay bỏ sót.

Danh mục edge case để nhắc agent

Agent sẽ bỏ sót edge case nếu bạn không liệt kê trục cần nghĩ. Đưa vào prompt một taxonomy:

  • Biên số học: 0, số âm, số cực lớn, tràn số, số thập phân.
  • Chuỗi/collection rỗng: "", [], {}, None.
  • Ranh giới điều kiện: đúng ngưỡng, ngay trên/dưới ngưỡng (off-by-one).
  • Kiểu dữ liệu sai: truyền string vào chỗ số.
  • Trạng thái đồng thời: gọi song song, race condition.
  • Lỗi dependency: DB timeout, API trả 500, mạng đứt.
  • Unicode/ký tự đặc biệt: emoji, ký tự có dấu, khoảng trắng.
# thêm vào prompt sinh test
Bắt buộc phủ các trục edge case sau nếu áp dụng được:
biên (0, âm, max), rỗng/None, off-by-one tại ngưỡng,
lỗi dependency (timeout/exception), input sai kiểu.

Mocking đúng cách cho integration

Integ test chạm nhiều thành phần; bạn muốn kiểm thứ của mình chứ không phải DB của người khác. Quy tắc: mock ranh giới ngoài (network, DB, clock, filesystem), giữ thật logic nội bộ.

from unittest.mock import patch

@patch("app.payment.gateway.charge")
def test_checkout_retries_on_timeout(mock_charge):
    # lần đầu timeout, lần hai thành công
    mock_charge.side_effect = [TimeoutError, ChargeResult(True, "tx1")]
    result = checkout(order)
    assert result.success
    assert mock_charge.call_count == 2   # kiểm hành vi retry

Đây là edge case chỉ integration test bắt được: logic retry. Agent cần biết dependency nào để mock — đó là lý do Bài 3 nhấn mạnh đưa signature dependency.

Các bước hướng dẫn agent phủ edge case

  • Đưa taxonomy edge case vào prompt như checklist bắt buộc.
  • Cho agent đọc code để tự phát hiện ngưỡng: mỗi if/elif, mỗi so sánh <=, mỗi raise là một edge case ứng viên.
  • Với integration: liệt kê rõ ranh giới cần mock và hành vi lỗi cần giả lập (timeout, exception).
  • Yêu cầu agent kiểm cả kết quả lẫn tương tác (số lần gọi, tham số truyền vào mock).
  • Dùng coverage nhánh để xác minh mọi raise/else đã được chạm.
  • Với hành vi thời gian (timeout, retry delay), mock time/clock để test chạy nhanh và tất định.

Khung tư duy: "Mỗi rẽ nhánh là một lời hứa cần kiểm"

Mỗi if, mỗi try/except, mỗi giá trị mặc định là một lời hứa hành vi code đưa ra. Test tồn tại để kiểm từng lời hứa đó. Quét code, đếm số nhánh, đó là số case tối thiểu. Agent tốt là agent đối chiếu số test với số nhánh và tự đòi thêm khi lệch.

Ví dụ off-by-one hay bị bỏ

def can_borrow(age):
    return age >= 18   # ngưỡng 18

Happy path test age=25 -> True, age=10 -> False: pass, coverage 100% line. Nhưng bug ranh giới ẩn ở age=18 và age=17. Agent được nhắc off-by-one sẽ sinh cả hai case đúng ngưỡng — điều test line-coverage không ép được.

Fixture và quản lý trạng thái trong integration test

Integ test thường cần trạng thái ban đầu: một user trong DB, một giỏ hàng có sẵn, một phiên đăng nhập. Hãy hướng dẫn agent dùng fixture để dựng và dọn trạng thái, đảm bảo mỗi test độc lập (independent). Nếu test A tạo dữ liệu mà test B ngầm phụ thuộc, chạy riêng lẻ B sẽ fail — một dạng lỗi phụ thuộc thứ tự rất khó chẩn đoán. Agent nên được nhắc: mỗi test tự dựng đủ trạng thái nó cần và dọn sạch sau khi chạy. Với DB, dùng transaction rollback hoặc database tạm cho mỗi test là mẫu hình an toàn.

Assert cả kết quả lẫn tương tác

Điểm khác biệt lớn nhất giữa unit test và integration test tốt là integration test kiểm cả tương tác giữa các thành phần. Ngoài kết quả cuối, hãy nhắc agent kiểm: dependency được gọi đúng số lần chưa, tham số truyền vào có đúng không, thứ tự gọi có hợp lý không. Chính những assertion tương tác này bắt được lớp bug mà unit test đơn lẻ bỏ sót — như gọi API thanh toán hai lần do lỗi logic, hay truyền sai đơn vị tiền tệ xuống gateway.

Sai lầm thường gặp

  • Mock luôn cả logic nội bộ: test pass nhưng chẳng kiểm gì thật, chỉ kiểm mock của chính mình.
  • Quên test nhánh lỗi của dependency: chỉ giả lập success, bỏ timeout/exception — đúng chỗ hay vỡ ở production.
  • Chỉ assert kết quả, bỏ assert tương tác: không phát hiện được lỗi retry gọi 5 lần thay vì 2.
  • Không mock thời gian: test retry chạy 30 giây thật, chậm và không tất định (flaky).
  • Bỏ qua off-by-one: line coverage 100% vẫn lọt bug ranh giới kinh điển.
Bài 6 ta chuyển sang câu hỏi sống còn: làm sao biết test agent sinh ra tốt, chứ không chỉ nhiều.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi