4.5.16 · Coding › Software Engineering
Intuition Core idea ek saanch mein
Tumhare tests pass ho rahe hain — lekin kya woh bugs actually pakad rahe hain ? Mutation testing iska jawab deta hai deliberately chhoti bugs (mutants) inject karke tumhare code mein, aur dekh ta hai ki tumhare tests fail hote hain ya nahi. Agar test suite tab bhi pass ho jaye jab tum ne code tod diya, toh suite so rahi hai duty par.
Intuition Code coverage ke saath problem
Coverage tumhe batata hai ki ek line execute hui, yeh nahi ki usse check kiya gaya. Socho:
def is_adult (age): return age >= 18
Ek test assert is_adult(20) == True us line ko run karta hai — 100% line coverage! — lekin woh tab bhi pass hoga agar code age > 18, age >= 17, ya seedha return True hota. Coverage tumhe ek jhoothi safety ka ehsaas deta hai.
WHY mutation testing yeh fix karta hai: "kya humne line run ki?" poochhne ki jagah, yeh poochhta hai "agar yeh line galat hoti, toh koi test notice karta?" Yahi woh asli sawaal hai jo ek test suite ko jawab dena chahiye.
Definition Key vocabulary
Mutant : tumhare program ki ek copy jisme ek mutation operator ke zariye ek chhoti si syntactic change apply ki gayi ho.
Mutation operator : ek rule jo change karta hai (jaise + → -, > → >=, True → False swap karna, ya koi statement delete karna).
Killed mutant : ek mutant jis par kam se kam ek test fail ho — tumhari suite ne inject ki gayi bug pakad li.
Survived mutant : bug ke bawajood sab tests pass ho jaate hain — tumhari suite ne usse miss kar diya.
Equivalent mutant : ek mutant jo syntactically alag hai lekin original se bilkul same behave karta hai , isliye koi test kabhi usse kill nahi kar sakta (false positive — ek jaana-maana pain point).
Worked example Loop ko haath se chalke dekhna
Original code:
def classify (n):
if n > 0 : # line A
return "pos"
return "nonpos"
Test suite:
assert classify( 5 ) == "pos"
assert classify( - 3 ) == "nonpos"
Mutant 1: line A mein n > 0 → n >= 0 badlo.
Yeh mutant kyun? > vs >= boundary ek classic off-by-one bug hai.
Tests run karo: classify(5)→"pos" ✓, classify(-3)→"nonpos" ✓. Dono pass → mutant SURVIVED. 😱
Woh survive kyun kiya? Humne kabhi n = 0 test nahi kiya, woh exact value jahan > aur >= differ karte hain.
Fix: assert classify(0) == "nonpos" add karo. Ab mutant 0 ke liye "pos" return karta hai → test fail hota hai → KILLED .
Mutant 2: return "pos" → return "nonpos" badlo.
Tests run karo: classify(5) ab "nonpos" return karta hai ≠ "pos" → test fails → KILLED immediately. Achha, humari suite yeh pehle se cover karti hai.
Worked example Score compute karna
Maano ek tool M = 50 mutants generate karta hai. Analysis mein E = 2 equivalent mutants milte hain. Tests K = 40 ko kill karte hain.
Score = 50 − 2 40 = 48 40 ≈ 0.833 = 83.3%
Pehle 2 kyun subtract karo? Woh 2 kabhi kill nahi ho sakte, isliye asli denominator (killable mutants) 48 hai, 50 nahi. 50 use karna tumhari suite ki sachchi quality ko understate karta (tumhe 40/50 = 80% milta).
Common mistake "High code coverage matlab mujhe mutation testing ki zaroorat nahi."
Kyun sahi lagta hai: coverage woh metric hai jo sab report karte hain, aur 100% sun ne mein complete lagta hai. Kami: coverage execution measure karta hai, assertion strength nahi. Ek test jisme koi asserts nahi hai woh 100% coverage hit kar sakta hai aur zero mutants kill kar sakta hai. Fix: coverage ko necessary-but-not-sufficient maano; mutation score woh part measure karta hai jo coverage ignore karta hai.
Common mistake "Ek surviving mutant ka matlab hai mera code buggy hai."
Kyun sahi lagta hai: "code toot gaya aur kuch fail nahi hua" khatarnak lagta hai. Kami: original code theek hai; survivor ek test mein gap dikhata hai, production code mein koi defect nahi (jab tak mutant equivalent na ho, us case mein woh kuch nahi dikhata). Fix: ek survivor ek test add karne ya strengthen karne ki instruction hai, code-fix ticket nahi.
Common mistake "Mujhe 100% mutation score target karna chahiye."
Kyun sahi lagta hai: perfect scores satisfying hote hain. Kami: equivalent mutants ki wajah se 100% aksar unreachable hai, aur aakhiri kuch percent achieve karne mein bahut effort lagta hai (diminishing returns). Fix (80/20): critical logic par high-value mutations ko target karo; accept karo ki kuch survivors equivalents ya low-risk hain.
Recall Aage padhne se pehle: predict karo ki yeh mutant survive karta hai ya nahi
Code: def max2(a,b): return a if a > b else b. Test: assert max2(3, 1) == 3.
Mutant: a > b → a < b. Survive hoga ya kill hoga?
Answer: max2(3,1) ke saath, mutant b = 1 ≠ 3 return karta hai → test fails → KILLED . Lekin mutant a > b → a >= b is test par survive kar jaata (usse kill karne ke liye a == b case chahiye, jaise max2(5,5)).
Mutation testing code mein kya inject karta hai? Chhoti deliberate bugs jisme mutants kehte hain, har ek ek mutation operator se banaya gaya hota hai.
Ek mutant "killed" kab hota hai? Jab suite ka kam se kam ek test us mutant par fail ho jaye.
Ek mutant "survive" kab karta hai? Jab inject ki gayi bug ke bawajood saare tests pass ho jaayein — test suite mein ek gap reveal hota hai.
Mutation score formula K / ( M − E ) = killed / (total mutants − equivalent mutants).
Denominator se equivalent mutants kyun subtract karte hain? Woh original se identically behave karte hain, isliye koi test kabhi unhe kill nahi kar sakta; unhe count karna score ko unfairly lower kar dega.
Code coverage ki woh key limitation kya hai jo mutation testing fix karta hai? Coverage prove karta hai ki ek line run hui, yeh nahi ki koi assertion us mein ek bug pakad paati.
Equivalent mutant kya hota hai? Ek syntactic change jo original program ke identical behaviour produce karta hai; use kabhi kill nahi kiya ja sakta.
Ek surviving (non-equivalent) mutant tumhe kya karne ko kehta hai? Ek test add karo ya strengthen karo — production code fix karne ki zaroorat nahi.
Ek mutation operator ka example do > → >=, + → -, True → False swap karo, ya koi statement delete karo.
Recall Feynman: ek 12-saal ke bachche ko explain karo
Socho tumne ek smoke alarm banaya aur tum jaanna chahte ho ki woh sach mein kaam karta hai ya nahi. Tum bas usse dekhte nahi — tum uske neeche ek chhoti si maacha jalate ho yeh dekhne ke liye ki woh chillaata hai ya nahi. Mutation testing tumhare tests ke saath yahi karta hai: woh secretly tumhare program ko chhote-chhote tareekon se todo (chhote "bug matches" jalao) aur dekhta hai ki tumhare tests chillaate hain (fail hote hain) ya nahi. Agar tumhare tests tab bhi chup rahein jab program toota hua ho, tumhare tests bure smoke alarms hain aur unhe fix karna hoga.
"M.K.S." → M utate the code, K ill it with a test, S core the survivors.
Aur score ke liye: "Killed over Killable" — tum sirf woh mutants count karte ho jo kill ho sakte thay (M − E ).
Unit testing — mutation testing tumhari likhi unit tests ko grade karta hai.
Code coverage — woh weaker metric jise mutation testing complement/expose karta hai.
Test-driven development — TDD pehle tests likhta hai; mutation testing unki strength audit karta hai.
Boundary value analysis — bahut saare surviving mutants boundary bugs hote hain (> vs >= ).
Continuous integration — jahan mutation runs hota hai (aksar nightly, kyunki yeh slow hota hai).
Fault injection — "deliberately todo aur observe karo" techniques ki broader family.
only checks line executed
applies tiny change to make