Thiết lập môi trường: runner, sandbox và bộ đo coverage
Agent sinh test chỉ mạnh khi nó chạy được test và đọc được kết quả. Bài này ta dựng bộ khung hạ tầng: test runner, môi trường cô lập (sandbox), và bộ đo coverage — ba thứ agent bắt buộc phải gọi được bằng lệnh và parse được output.
1. Chọn test runner có output máy đọc được
Agent cần output có cấu trúc, không phải log người-đọc. Với Python dùng pytest kèm JSON report; với JS dùng jest với --json.
# Python: cài công cụ
pip install pytest pytest-json-report pytest-cov
# Chạy và xuất JSON để agent parse
pytest --json-report --json-report-file=report.json \
--cov=src --cov-report=json:cov.json
Agent đọc report.json để biết test nào fail (kèm traceback), và cov.json để biết dòng/nhánh nào chưa được phủ. Đây là hai tín hiệu phản hồi cốt lõi.
2. Cô lập bằng sandbox
Test do LLM sinh có thể chứa code nguy hiểm ngoài ý muốn: xóa file, gọi mạng, vòng lặp vô hạn. Không bao giờ chạy trực tiếp trên máy host của bạn.
# Chạy test trong container cô lập, giới hạn mạng + thời gian
docker run --rm --network none \
--memory=512m --cpus=1 \
-v $(pwd):/app -w /app \
python:3.11 timeout 60 pytest --json-report
Ba lớp bảo vệ tối thiểu: --network none (chặn gọi mạng), timeout (chặn treo), giới hạn RAM/CPU (chặn ngốn tài nguyên). Với các nền tảng như E2B hay Firecracker microVM bạn có sandbox mạnh hơn, nhưng Docker là đủ để bắt đầu.
3. Cấu hình coverage đo cả nhánh (branch), không chỉ dòng
Coverage theo dòng (line) dễ đánh lừa: một dòng if x: do() được tính là phủ dù bạn chưa từng test nhánh else. Luôn bật branch coverage.
# file: .coveragerc
[run]
branch = True
source = src
[report]
show_missing = True
fail_under = 80
Các bước thiết lập đầy đủ
- Cài runner + plugin JSON + plugin coverage.
- Viết một wrapper script để agent gọi một lệnh duy nhất và nhận về JSON gộp:
{passed, failed, tracebacks, uncovered_lines}. - Đóng gói môi trường chạy vào Docker image có sẵn dependencies của dự án (tránh lỗi "module not found" giả).
- Thêm
--network none+timeout+ giới hạn tài nguyên. - Bật branch coverage và đặt
fail_underlàm ngưỡng dừng. - Test thử wrapper bằng một test viết tay để chắc chắn output parse đúng trước khi cắm LLM vào.
Ví dụ wrapper output mà agent nhận
{
"passed": 4,
"failed": 1,
"failures": [
{"test": "test_negative_price",
"error": "AssertionError: expected ValueError"}
],
"uncovered": {"src/discount.py": [3, 4]}
}
Có cấu trúc này, agent biết chính xác: sửa test test_negative_price, và viết thêm test chạm dòng 3-4.
Vì sao wrapper gộp lại quan trọng đến vậy
Nhiều người để agent tự đọc log thô của pytest rồi tự suy diễn. Đây là nguồn lỗi âm thầm: định dạng log thay đổi giữa các phiên bản, màu ANSI lẫn vào, traceback dài làm loãng tín hiệu và tốn token. Một wrapper chuẩn hóa output về JSON gộp mang lại ba lợi ích: agent nhận đúng tín hiệu cần thiết (fail nào, dòng nào chưa phủ), chi phí token thấp hơn nhiều vì bỏ phần nhiễu, và bạn có một điểm kiểm soát duy nhất để thêm số liệu mới (ví dụ mutation score ở bài sau) mà không phải sửa logic agent. Hãy coi wrapper là hợp đồng (contract) giữa agent và môi trường: agent chỉ cần biết hình dạng JSON, không cần biết bên dưới chạy pytest hay jest.
Kiểm chứng môi trường trước khi cắm LLM
Một bước hay bị bỏ qua: verify wrapper bằng một test viết tay đã biết trước kết quả. Viết một test chắc chắn pass và một test chắc chắn fail, chạy qua wrapper, và kiểm output JSON có phản ánh đúng không. Nếu wrapper báo sai ngay ở test viết tay, thì mọi thứ agent sinh sau này đều dựa trên nền móng lỗi. Đầu tư 15 phút ở đây tiết kiệm hàng giờ debug vòng lặp agent sau này.
Checklist môi trường sẵn sàng
- [ ] Chạy một lệnh ra JSON gộp pass/fail/traceback/coverage.
- [ ] Test chạy trong container, không đụng host.
- [ ] Có
--network nonevàtimeout. - [ ] Branch coverage đã bật.
- [ ] Dependencies của dự án có sẵn trong image.
- [ ] Đã verify wrapper bằng test viết tay.
Sai lầm thường gặp
- Chạy test LLM sinh trên máy thật: một dòng
shutil.rmtree('/')trong fixture là đủ gây thảm họa. - Chỉ đo line coverage: đạt 90% line nhưng bỏ hết nhánh lỗi, agent tự tin sai.
- Không giới hạn timeout: test có vòng lặp vô hạn treo cả pipeline.
- Quên cài dependency trong sandbox: agent nhận lỗi import giả, tưởng code sai và sinh test lung tung để "chữa".
- Parse output bằng regex trên log người-đọc: dễ vỡ; hãy dùng định dạng JSON.