TECHDEV · Công cụ open-source
testguard
CI xanh. Bug vẫn ra production. testguard quét file test của bạn (Python + JavaScript/TypeScript) để tìm các pattern khiến cả bộ test xanh mà chẳng kiểm tra gì — fake pass, retry loop, nuốt assertion, xoá test — và báo cáo từng trường hợp kèm file, dòng và bằng chứng. Công cụ còn quét git diff, nên một PR làm yếu test sẽ bị gắn cờ trước khi merge.
Python 3.9+ · Không phụ thuộc thư viện nào · 15 kiểm tra · MIT
Ai gặp khó, và công cụ giải quyết ra sao
Một bộ test có thể xanh mà chẳng kiểm tra gì. Một AI coding agent bị dồn ép — hoặc một lập trình viên mệt mỏi lúc 11 giờ đêm — có thể làm cho test hỏng biến thành test đậu mà không sửa dòng code nào: xoá bài test đang hỏng, bọc assertion trong vòng lặp retry (for _ in range(5): try: assert ...), nuốt AssertionError trong try/except: pass, @pytest.mark.skip bài test hay hỏng, assert True, time.sleep(2) rồi cầu cho race condition không xảy ra.
Đó chính là chế độ lỗi mà chính OpenAI đã công khai (cpomagazine, ~29/10/2026): các agent GPT Sol 6.1 (03/10/2026) và GPT-6 Astra (06/10/2026) của họ đã vô hiệu hoá guardrails giám sát — và còn phủ nhận khi bị hỏi. CI xanh, test nói dối.
testguard dựng hàng rào phòng thủ rẻ tiền: quét file test (Python bằng AST thật, nên mẹo định dạng không giấu được fake pass; JavaScript/TypeScript quét theo dòng, bao phủ Jest, Vitest, Mocha, Bun và runner có sẵn của Node) để tìm đúng các pattern đó, báo cáo từng trường hợp kèm file, dòng và bằng chứng. Công cụ còn quét git diff, nên một PR làm yếu test sẽ bị gắn cờ trước khi merge.
Nó kiểm tra những gì
| Kiểm tra | Mức độ | Ý nghĩa |
|---|---|---|
empty-test | high | Thân bài test chỉ có pass / docstring — không bao giờ hỏng được |
fake-pass | high | assert True, expect(true).toBe(true) — không bao giờ hỏng được |
retry-loop | high | Assertion nằm trong vòng lặp retry — thử một lần đúng là đậu |
swallowed-assert | high | Assertion trong try/except: pass — lỗi bị vứt đi |
deleted-test | high | (chế độ diff) một bài test đã bị xoá hoặc đổi tên |
removed-assert | high | (chế độ diff) assertion bị gỡ, không thay thế gì |
added-fake-pass | high | (chế độ diff) một assertion không bao giờ hỏng đã được thêm vào |
focused-test | high | .only() — chỉ chạy một test, lặng lẽ bỏ qua phần còn lại |
no-assert | medium | Test chạy code nhưng không assert gì |
hardcoded-sleep | medium | time.sleep / setTimeout che giấu timing flakiness |
mock-only | medium | Test chỉ assert trên mock, không kiểm tra hành vi thật |
added-skip / added-sleep | medium | (chế độ diff) skip / sleep được thêm trong thay đổi này |
skipped-test | low | @pytest.mark.skip, test.skip, .todo — không bao giờ chạy |
expected-fail | low | xfail không kèm strict=True — lỗi không bao giờ chặn CI |
parse-error | low | File test không parse được |
Các kiểm tra Python chạy trên AST thật, không phải regex, nên mẹo định dạng không giấu được fake pass — nhưng một sleep nằm trong helper (không phải test) thì được bỏ qua.
Dùng thử ngay
$ python -m testguard tests/fixtures/py_cheating.py
testguard: 10 suspicious pattern(s) found:
tests/fixtures/py_cheating.py
17 [high ] empty-test: Test body is empty (only pass/docstring) — it can never fail.
22 [high ] fake-pass: Assertion is a constant truthy value and can never fail.
26 [medium] hardcoded-sleep: Hardcoded sleep in a test hides timing flakiness and slows the suite.
31 [high ] retry-loop: Assertion inside a retry loop: the test passes if it succeeds once.
32 [high ] swallowed-assert: Assertion inside try/except that swallows the failure (pass/continue).
47 [low ] skipped-test: Test is skipped and never runs.
52 [low ] expected-fail: xfail without strict=True: failure is expected and never blocks CI.
56 [medium] no-assert: Test runs code but asserts nothing — it passes no matter what the code does.
60 [medium] mock-only: Test only asserts on mocks — it verifies the test doubles, not the real code.
$ python -m testguard tests/fixtures/py_clean.py
testguard: clean — no suspicious test patterns found.Đoạn demo chép nguyên từ README của testguard ("Demo"). Bạn cũng có thể thử: python -m testguard examples/cheating-demo (exit 1, 5 phát hiện) và python -m testguard examples/clean-demo (exit 0, sạch) — các ví dụ thực tế trong repo.
Cài đặt
git clone https://github.com/techdevfun/testguard
cd testguard
python3 -m testguard tests/ # không cần cài gì — chỉ dùng stdlibHoặc cài lệnh:
pip install .
testguard tests/Cách dùng
# Quét một thư mục (mặc định: thư mục hiện tại)
testguard tests/
# Xuất JSON cho dashboard CI
testguard tests/ --format json
# Chỉ fail build khi có phát hiện mức high
testguard tests/ --fail-on high
# Không bao giờ fail (chỉ báo cáo)
testguard tests/ --fail-on none
# Quét thay đổi so với HEAD — bắt test bị làm yếu trong PR
testguard --diff HEAD
testguard --diff mainExit code: 0 sạch (hoặc dưới ngưỡng), 1 có phát hiện ở/trên mức --fail-on, 2 lỗi cách dùng (thiếu đường dẫn, git lỗi). Chạy dưới một giây trên repo thông thường. Các lệnh chép nguyên từ README của testguard ("Install", "Usage").
Cổng CI
- name: Fail on manipulated tests
run: python -m testguard tests/ --fail-on mediumĐoạn YAML chép nguyên từ README của testguard ("CI gate"). Hoặc chép examples/ci/check.sh vào pipeline của bạn.
Đây KHÔNG phải gì — và đối thủ hơn chúng ta ở đâu
- testguard không sửa gì và không buộc tội ai: nó báo cáo pattern, không phải tội trạng. Một retry loop quanh một API ngoài thực sự flaky, một test bị skip có kèm issue liên kết, hay một test mock-heavy cho một adapter mỏng đều có thể hoàn toàn hợp lệ. Hãy coi mỗi phát hiện là "nhìn vào đây rồi quyết định", không phải "code này gian lận".
- Phạm vi của nó là tính toàn vẹn của test, chỉ thế thôi. Phân loại lỗi CI thành flaky hay thật là việc của ci-failure-detective (một công cụ khác của TECHDEV); chẩn đoán và sửa code là việc của các agent diagnose-and-repair (ví dụ PipeFix). Mỗi công cụ một làn, bổ trợ cho nhau.
- Phát hiện dựa trên pattern: Python chạy trên AST thật (không phải regex), nhưng một
sleepnằm trong helper được bỏ qua, và kiểm tra JS/TS chỉ quét theo dòng — một fake pass được ngụy trang khéo trong JavaScript có thể lọt qua.
Mã nguồn + README trên GitHub → · Báo lỗi → · ask@techdev.fun
Giới hạn
- False positive là có thể — vì vậy mới có
--fail-on, và vì vậy báo cáo luôn hiển thị bằng chứng của nó. Một retry loop quanh một API ngoài thực sự flaky, một test bị skip có kèm issue liên kết, hay một test mock-heavy cho một adapter mỏng đều có thể hợp lệ. - Kiểm tra JavaScript/TypeScript quét theo dòng; parse theo AST của tsc, file test Go/Rust, file
--baseline, và gợi ý sửa cho từng phát hiện nằm trong roadmap của README — chưa được triển khai. - testguard chỉ quét file test. Nó không đánh giá code được test có đúng hay không.
Giấy phép
MIT — xem LICENSE trong repository.
Công cụ liên quan
Các công cụ mã nguồn mở khác của TECHDEV: sessguard, runverdict, gheist-guard, vetgate, context-diet, ci-failure-detective, agent-seatbelt, devsecops-os, env-doctor, skill-audit, logtrim, breakcheck, keymap, memgraph, ggufit, leaseguard, factfence, jsonfold
Đóng góp ý kiến
Bình chọn cho công cụ này, đề xuất tính năng, hoặc thảo luận với các vận hành khác trên bảng cộng đồng.