Cung cấp context: agent cần biết gì để sinh test đúng
Chất lượng test agent sinh ra tỷ lệ thuận với chất lượng context bạn đưa vào. Đưa mỗi hàm trần trụi, agent sẽ đoán mò kiểu dữ liệu và bịa assertion. Bài này dạy cách gom đúng và đủ context — không thừa (tốn token, nhiễu) không thiếu (agent đoán bừa).
Context tối thiểu cho một đơn vị test
Để sinh test cho một hàm, agent cần:
- Mã nguồn hàm cần test.
- Chữ ký + type hints / docstring để biết input/output kỳ vọng.
- Các dependency được gọi (hàm/class con) để biết cần mock gì.
- Ví dụ cách hàm được gọi trong codebase (call sites) để hiểu ngữ cảnh dùng thật.
- Convention test của dự án: framework, cách đặt tên, fixture chung.
Ví dụ: context nghèo vs context giàu
Context nghèo:
def process(order):
return service.charge(order.total)
Agent không biết order là gì, service.charge trả về gì. Nó sẽ bịa. Context giàu bổ sung:
# Type: Order(total: Decimal, id: str)
# service.charge(amount) -> ChargeResult(success: bool, tx_id: str)
# raises PaymentError khi số dư không đủ
# Call site: process(cart.to_order()) # total luôn > 0
Giờ agent biết cần mock service.charge, test cả case success=True/False và case PaymentError.
Kỹ thuật gom context tự động
- Parse AST để trích chữ ký hàm, các import, và tên các hàm được gọi bên trong.
- Truy vết dependency 1 cấp: lấy định nghĩa của hàm/class được gọi trực tiếp (không lấy cả cây, tránh phình context).
- Grep call sites: tìm nơi hàm được gọi để đưa ví dụ dùng thật.
- Đọc file test mẫu đã có trong dự án để agent bắt chước convention.
- Nén thông minh: với dependency dài, chỉ đưa chữ ký + docstring thay vì toàn bộ thân hàm.
# pseudocode gom context
def build_context(func):
ctx = read_source(func)
ctx += extract_signature_and_docstring(func)
for dep in first_level_deps(func): # chỉ 1 cấp
ctx += signature_only(dep) # nén
ctx += grep_call_sites(func, limit=3)
ctx += pick_sample_test_file() # học convention
return ctx
Khung tư duy: "Đủ để một junior viết được test"
Hãy tự hỏi: nếu đưa đúng context này cho một lập trình viên junior chưa biết codebase, họ có viết được test đúng không? Nếu họ vẫn phải hỏi "hàm này trả về gì?" thì context còn thiếu. Nếu bạn đưa cả module 2000 dòng thì context thừa — junior cũng lạc.
Cân bằng token
- Với hàm thuần (pure function): context nhỏ, chỉ cần source + type.
- Với hàm có side effect (DB, network, file): phải kèm signature của dependency để agent biết mock gì.
- Với integration test: cần thêm sơ đồ luồng và fixture khởi tạo trạng thái.
Chống "context rot": chất lượng quan trọng hơn số lượng
Một hiểu lầm phổ biến là "càng nhiều context càng tốt". Thực tế ngược lại: khi prompt phình lên, tín hiệu quan trọng bị chôn giữa hàng nghìn dòng không liên quan, và mô hình dễ lạc — hiện tượng gọi là context rot. Với một hàm 20 dòng, đừng đưa cả module 2000 dòng; hãy đưa đúng 20 dòng đó cộng vài chục dòng dependency đã nén. Kết quả là test chính xác hơn, chi phí thấp hơn, và agent ít bịa test cho những hàm không hề được yêu cầu. Nguyên tắc vàng: mỗi dòng context bạn thêm vào phải trả lời được câu hỏi "dòng này giúp viết test đúng hơn ở điểm nào?". Nếu không trả lời được, hãy bỏ nó ra.
Ví dụ nén dependency đúng cách
Giả sử hàm cần test gọi tới một class PaymentGateway dài 300 dòng. Đừng dán cả 300 dòng. Thay vào đó, chỉ đưa chữ ký các phương thức được gọi và docstring của chúng: charge(amount: Decimal) -> ChargeResult; raises PaymentError. Agent chỉ cần biết giao diện của dependency để mock đúng, không cần biết cài đặt bên trong. Việc nén này vừa giữ context gọn vừa buộc agent mock thay vì gọi thật.
Sai lầm thường gặp
- Nhồi cả file/cả repo vào prompt: token đắt, agent nhiễu, dễ bịa test cho hàm không liên quan.
- Quên đưa type/docstring: agent đoán sai kiểu, assertion sai, test pass giả hoặc fail giả.
- Không đưa dependency signature: agent không biết mock gì, viết test gọi thật vào DB/API.
- Bỏ qua convention dự án: test sinh ra dùng
unittesttrong khi dự án dùngpytest, không khớp fixture chung. - Đưa call site sai: lấy nhầm ví dụ ở test cũ đã lỗi thời, agent học theo cái sai.