🔍검색

Trang chủ › Hướng dẫn

False kill trong mutation testing: baseline, truy cập tệp, lý do kill

Đăng ngày 2026-10-27

Mutation testing cố ý thay đổi code của bạn (< thành <=, return x thành return None) rồi kiểm tra xem các test có phát hiện ra không. Một mutant "killed" nghĩa là có một test thất bại. Nhưng một test có thể thất bại vì những lý do chẳng liên quan gì đến đột biến — và khi đó điểm mutation của bạn trông tốt hơn so với chất lượng thật của các test. Hướng dẫn này nêu ba cách kiểm tra để bắt các false kill (kill giả) này.

Vì sao false kill xảy ra

Nguyên nhânBạn thấy gì
Bộ test đã thất bại hoặc chập chờn (flaky) từ trước khi đột biếnNhiều kill với cùng một lỗi không liên quan
Test import tệp gốc, không phải bản sao đã đột biếnKill chỉ đến từ timeout hoặc lỗi môi trường
Timeout do máy chậmKill tập trung ở các test chạy lâu
Trạng thái dùng chung giữa các lần chạy (tệp tạm, cache, cổng)Kết quả kill thay đổi khi bạn chạy lại cùng một mutant

Timeout ảnh hưởng đến điểm số: trong Stryker, detected = killed + timeout, và mutation score = detected / valid × 100. Một máy timeout nhiều sẽ làm điểm bị thổi phồng. Nguồn: Stryker mutant states and metrics, kiểm tra 2026-10-19.

Kiểm tra 1 — Baseline trên bản sao nguyên bản

  1. Sao chép dự án sang một thư mục mới, đúng như cách công cụ mutation sẽ làm (cùng sandbox, cùng biến môi trường).
  2. Chạy toàn bộ bộ test ở đó mà không có đột biến, ba lần.
  3. Cả ba lần đều phải vượt qua. Bất kỳ thất bại nào ở đây là một thất bại có sẵn từ trước, về sau sẽ hiện ra thành các kill giả — hãy sửa hoặc cách ly nó trước.
cp -r project /tmp/mt-baseline && cd /tmp/mt-baseline
for i in 1 2 3; do pytest -q -p no:cacheprovider || echo "BASELINE FAIL $i"; done

Kiểm tra 2 — Chứng minh các test đã đọc tệp bị đột biến

Nếu một test không bao giờ nạp tệp bị đột biến thì nó không thể đã kill mutant đó. Hãy ghi lại việc truy cập tệp trong mỗi lần chạy mutant. Trong Python, một audit hook là đủ:

# conftest.py
import sys, os
TARGET = os.environ.get("MUT_TARGET")  # path of the mutated file
def hook(event, args):
    if event == "open" and TARGET and str(args[0]).endswith(TARGET):
        open("/tmp/mut_touched", "a").write("1")
sys.addaudithook(hook)

Chạy mỗi mutant sau khi đã xóa bytecode cũ (xóa __pycache__ trong bản sao) để tệp nguồn thực sự được đọc. Quy tắc: một kill mà tệp mục tiêu chưa từng được mở thì bị đánh dấu là suspect (đáng ngờ) và chạy lại. Trên các nền tảng khác, strace -e trace=openat (Linux) hoặc dữ liệu coverage của test runner cũng phục vụ cùng mục đích.

Kiểm tra 3 — Xem phân bố lý do kill

  1. Với mỗi mutant bị kill, lưu lại vì sao lần chạy thất bại: assertion error, exception khác, timeout, crash, lỗi collection/import.
  2. Lập một bảng đếm đơn giản.
Lý doMẫu lành mạnhDấu hiệu đáng ngờ
Assertion thất bạiPhần lớn các kill—
Exception khácMột sốMột thông báo giống hệt nhau trên nhiều mutant
TimeoutÍt (các mutant gây vòng lặp vô hạn)Chiếm tỷ lệ lớn; tăng lên trên máy chậm hơn
Lỗi import / collectionGần như không cóBất kỳ tỷ lệ đáng kể nào — chính harness, chứ không phải các test, đang thất bại

Tổng hợp lại

Câu hỏi thường gặp

Tôi có nên tăng timeout không?
Hãy đặt nó dựa trên baseline: đo thời gian bình thường của bộ test, rồi cho phép một bội số của nó. Quá thấp tạo ra kill giả; quá cao làm chậm lần chạy.
Lỗi biên dịch có được tính không?
Trong Stryker, lỗi biên dịch và lỗi thời gian chạy là các mutant "invalid" và không nằm trong điểm số.

Nguồn

Liên quan (tiếng Hàn): Tổng hợp kiến thức phát triển (개발 지식 모음, tiếng Hàn)

🌐 English · Español · Bahasa Indonesia · 日本語 · ไทย · Tiếng Việt · 中文

📤 공유