🔍검색

หน้าแรก › คู่มือ

False Kill ใน Mutation Testing: Baseline การเข้าถึงไฟล์ และเหตุผลที่ Kill

เผยแพร่ 2026-10-27

Mutation testing คือการเปลี่ยนโค้ดของคุณโดยตั้งใจ (เช่น < กลายเป็น <= หรือ return x กลายเป็น return None) แล้วตรวจว่าเทสต์ของคุณจับความเปลี่ยนแปลงได้หรือไม่ มิวแทนต์ที่ "killed" หมายถึงมีเทสต์ล้มเหลว แต่เทสต์อาจล้มเหลวด้วยเหตุผลที่ไม่เกี่ยวกับการกลายพันธุ์เลย และเมื่อเป็นเช่นนั้น คะแนน mutation ของคุณจะดูดีกว่าคุณภาพเทสต์จริง คู่มือนี้แสดงสามการตรวจสอบที่จับ false kill เหล่านี้ได้

ทำไมจึงเกิด false kill

สาเหตุสิ่งที่คุณเห็น
ชุดเทสต์ล้มเหลวหรือไม่เสถียรอยู่แล้วก่อนทำ mutationมี kill จำนวนมากที่เกิดจากข้อผิดพลาดเดียวกันที่ไม่เกี่ยวข้อง
เทสต์ import ไฟล์ต้นฉบับ ไม่ใช่สำเนาที่ถูกกลายพันธุ์kill เกิดจาก timeout หรือข้อผิดพลาดของสภาพแวดล้อมเท่านั้น
Timeout จากเครื่องที่ช้าkill กระจุกตัวอยู่ที่เทสต์ที่รันนาน
สถานะที่ใช้ร่วมกันระหว่างการรัน (ไฟล์ชั่วคราว แคช พอร์ต)ผล kill เปลี่ยนเมื่อรันมิวแทนต์ตัวเดิมซ้ำ

Timeout มีผลต่อคะแนน: ใน Stryker, detected = killed + timeout และ mutation score = detected / valid × 100 เครื่องที่ timeout บ่อยจะทำให้คะแนนสูงเกินจริง ที่มา: Stryker mutant states and metrics ตรวจสอบเมื่อ 2026-10-19

การตรวจที่ 1 — Baseline บนสำเนาที่สะอาด

  1. คัดลอกโปรเจกต์ไปยังไดเรกทอรีใหม่ ให้เหมือนกับที่เครื่องมือ mutation จะทำทุกประการ (sandbox เดียวกัน ตัวแปรสภาพแวดล้อมเดียวกัน)
  2. รันชุดเทสต์ทั้งหมดที่นั่นโดยไม่ทำ mutation สามครั้ง
  3. ต้องผ่านทั้งสามครั้ง ความล้มเหลวใด ๆ ตรงนี้คือความล้มเหลวที่มีอยู่ก่อนแล้ว ซึ่งจะปรากฏเป็น kill ปลอมในภายหลัง ให้แก้หรือแยกกักกันก่อน
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

การตรวจที่ 2 — พิสูจน์ว่าเทสต์อ่านไฟล์ที่ถูกกลายพันธุ์

หากเทสต์ไม่เคยโหลดไฟล์ที่ถูกกลายพันธุ์ ก็ไม่สามารถ kill มิวแทนต์นั้นได้ ให้บันทึกการเข้าถึงไฟล์ระหว่างการรันมิวแทนต์แต่ละตัว ใน Python ใช้ audit hook ก็เพียงพอ:

# 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)

รันมิวแทนต์แต่ละตัวโดยลบ bytecode เก่าออก (ลบ __pycache__ ในสำเนา) เพื่อให้ไฟล์ต้นฉบับถูกอ่านจริง กฎคือ: kill ที่ไฟล์เป้าหมายไม่เคยถูกเปิดให้ทำเครื่องหมายเป็น suspect แล้วรันซ้ำ สำหรับสแตกอื่น strace -e trace=openat (Linux) หรือข้อมูล coverage ของตัวรันเทสต์ใช้ได้เช่นกัน

การตรวจที่ 3 — ดูการกระจายของเหตุผลที่ kill

  1. สำหรับมิวแทนต์ที่ถูก kill ทุกตัว ให้เก็บเหตุผลที่การรันล้มเหลว: assertion error, exception อื่น, timeout, crash, ข้อผิดพลาดตอน collection/import
  2. ทำตารางนับอย่างง่าย
เหตุผลรูปแบบที่ปกติสัญญาณเตือน
Assertion ล้มเหลวkill ส่วนใหญ่—
Exception อื่นบ้างข้อความเดียวกันซ้ำในมิวแทนต์จำนวนมาก
Timeoutน้อย (มิวแทนต์ที่ทำให้เกิดลูปไม่รู้จบ)สัดส่วนมาก เพิ่มขึ้นบนเครื่องที่ช้ากว่า
ข้อผิดพลาดตอน import / collectionแทบไม่มีสัดส่วนที่มีนัยสำคัญ ไม่ว่าเท่าไร แปลว่าตัวชุดทดสอบ ไม่ใช่เทสต์ ที่กำลังล้มเหลว

สรุปรวมกัน

คำถามที่พบบ่อย

ควรเพิ่ม timeout หรือไม่
ตั้งจาก baseline: วัดเวลารันชุดเทสต์ปกติ แล้วให้เวลาเป็นจำนวนเท่าของค่านั้น ตั้งต่ำเกินไปจะเกิด kill ปลอม สูงเกินไปจะทำให้รันช้า
ข้อผิดพลาดตอนคอมไพล์นับด้วยหรือไม่
ใน Stryker ข้อผิดพลาดตอนคอมไพล์และตอนรันไทม์ถือเป็นมิวแทนต์ "invalid" และไม่นับรวมในคะแนน

แหล่งที่มา

ที่เกี่ยวข้อง (ภาษาเกาหลี): รวมความรู้การพัฒนา (개발 지식 모음)

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

📤 공유