Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Mutation testing ile test suite kalitesini olc. Stryker, mutmut, go-mutesting destegi.
.claude/skills/mutation-testing/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 132% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 114% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 103% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 123% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 116% | 0% |
Mutation testing, test suite'inin kalitesini olcen bir tekniktir. Kaynak kodda kucuk degisiklikler (mutasyonlar) yapilir ve testlerin bu degisiklikleri yakalayip yakalamadigina bakilir.
Code coverage "kodun ne kadari calistiriliyor?" sorusunu yanitlar. Mutation testing "testler gercekten bir seyi kontrol ediyor mu?" sorusunu yanitlar.
%100 code coverage'a sahip ama assertion'i olmayan testler mutation testing'de FAIL alir.
bash# Install npm install --save-dev @stryker-mutator/core npx stryker init # Jest runner npm install --save-dev @stryker-mutator/jest-runner # Vitest runner npm install --save-dev @stryker-mutator/vitest-runner # TypeScript support npm install --save-dev @stryker-mutator/typescript-checker
Config (stryker.config.mjs):
javascript/** @type {import('@stryker-mutator/api/core').PartialStrykerOptions} */ export default { mutate: [ 'src/**/*.ts', '!src/**/*.test.ts', '!src/**/*.spec.ts', '!src/**/*.d.ts', '!src/**/index.ts' ], testRunner: 'jest', checkers: ['typescript'], reporters: ['html', 'clear-text', 'progress', 'json'], coverageAnalysis: 'perTest', thresholds: { high: 80, low: 60, break: null // Set to 60 to fail CI on low kill ratio }, timeoutMS: 60000, concurrency: 4 };
Run:
bashnpx stryker run # Report: reports/mutation/mutation.html
bashpip install mutmut
Config (pyproject.toml):
toml[tool.mutmut] paths_to_mutate = "src/" tests_dir = "tests/" runner = "python -m pytest -x --tb=short -q" dict_synonyms = "Struct,NamedStruct"
Run:
bash# Full run mutmut run # Results mutmut results # Show specific mutant mutmut show 42 # HTML report mutmut html
bashgo install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest
Run:
bash# Full run go-mutesting ./... # Specific package go-mutesting ./pkg/calculator/... # With score threshold go-mutesting --score 0.8 ./...
a + b -> a - b, a * b, a / b
a * b -> a / b, a + b
a++ -> a--Neyi test eder: Matematiksel hesaplamalarin dogrulugu
a > b -> a >= b
a < b -> a <= b
a >= b -> a > b
a <= b -> a < bNeyi test eder: Boundary condition'lar, off-by-one hatalari
true -> false
a && b -> a || b
a || b -> a && b
!a -> aNeyi test eder: Boolean logic, branch coverage
if (condition) -> if (!condition)
while (x > 0) -> while (x <= 0)Neyi test eder: Kontrol akisinin dogrulugu
return x -> return 0
return true -> return false
return "hello" -> return ""
return obj -> return nullNeyi test eder: Return value assertion'lari
"hello" -> ""
"hello" -> "Stryker was here!"Neyi test eder: String handling, empty string kontrolu
doSomething(); -> (removed)
x = calculate() -> (removed)Neyi test eder: Side effect'lerin test edilip edilmedigi
| Seviye | Kill Ratio | Anlami | |--------|-----------|--------| | Mukemmel | 90%+ | Test suite cok guclu | | Iyi | 80-89% | Kabul edilebilir, kucuk iyilestirmeler | | Orta | 60-79% | Ciddi iyilestirme gerekli | | Zayif | < 60% | Test suite guvenilemez |
Hedef: Her projede minimum %80 kill ratio
Bir mutant survive ettiyse su adimlari takip et:
Dosya: src/calculator.ts:15
Original: if (balance > 0) { ... }
Mutant: if (balance >= 0) { ... }
Durum: SURVIVEDbalance === 0 durumunu test etmiyortypescriptit('should handle zero balance', () => { const result = processBalance(0); expect(result).toBe('no_funds'); // Bu test mutant'i oldurur });
bashnpx stryker run --mutate "src/calculator.ts"
Survived mutant > -> >= ise:
typescript// Her boundary icin 3 test yaz: altinda, ustunde, tam sinirda it('rejects when below minimum', () => expect(validate(-1)).toBe(false)); it('rejects at exact minimum', () => expect(validate(0)).toBe(false)); it('accepts above minimum', () => expect(validate(1)).toBe(true));
Survived mutant return x -> return 0 ise:
typescript// Testlerde return value'yu MUTLAKA assert et const result = calculate(5, 3); expect(result).toBe(8); // Spesifik deger kontrolu
Survived mutant && -> || ise:
typescript// Her boolean kombinasyonu test et it('fails when only A is true', () => expect(check(true, false)).toBe(false)); it('fails when only B is true', () => expect(check(false, true)).toBe(false)); it('passes when both are true', () => expect(check(true, true)).toBe(true)); it('fails when both are false', () => expect(check(false, false)).toBe(false));
Survived mutant statement removal ise:
typescript// Side effect'leri de test et calculate(5); expect(mockLogger.info).toHaveBeenCalledWith('Calculated: 5'); expect(mockMetrics.increment).toHaveBeenCalledWith('calculations');
Survived mutant !x -> x ise:
typescript// Her iki yolu da test et it('handles truthy input', () => expect(process(true)).toBe('A')); it('handles falsy input', () => expect(process(false)).toBe('B'));
yamlname: Mutation Testing on: pull_request: branches: [main] schedule: - cron: '0 2 * * 0' # Haftalik tam tarama jobs: mutation-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - run: npm ci - run: npx stryker run - uses: actions/upload-artifact@v4 with: name: mutation-report path: reports/mutation/ - name: Check kill ratio run: | SCORE=$(cat reports/mutation/mutation.json | jq '.schemaVersion' -r) # Custom threshold check script
yamlmutation-test: stage: test script: - npm ci - npx stryker run artifacts: paths: - reports/mutation/ expire_in: 7 days only: - merge_requests allow_failure: true # Ilk baslarken, sonra kaldir
PR'larda sadece degisen dosyalari mutate et:
yaml- name: Get changed files id: changed run: | FILES=$(git diff --name-only origin/main...HEAD -- '*.ts' | grep -v test | tr '\n' ',') echo "files=$FILES" >> $GITHUB_OUTPUT - name: Run incremental mutation if: steps.changed.outputs.files != '' run: npx stryker run --mutate "${{ steps.changed.outputs.files }}"
Sadece degisen dosyalari mutate et:
bash# Stryker npx stryker run --mutate "src/changed-file.ts" # mutmut mutmut run --paths-to-mutate src/changed_module/
Stryker'da coverageAnalysis: 'perTest' kullan. Her mutant sadece ilgili testlerle calistirilir.
Sonsuz donguye giren mutant'lar icin makul timeout:
javascripttimeoutMS: 60000, // 60 saniye max timeoutFactor: 1.5 // Normal surenin 1.5 kati
CPU sayisina gore paralel calistir:
javascriptconcurrency: 4 // veya os.cpus().length - 1
Onceki sonuclari cache'le:
javascriptincremental: true, incrementalFile: 'reports/stryker-incremental.json'
Bazi mutasyonlar kodun davranisini degistirmez:
typescript// Original const i = 0; // Mutant (equivalent - davranis ayni) const i = -0;
Cozum: Equivalent mutant'lari rapordan cikar, survived olarak sayma.
while (true) veya for(;;) gibi durumlar: Cozum: Timeout ayarini dogru yap, timeout mutant'larini "killed" say.
Buyuk codebase'lerde saatlerce surebilir: Cozum: Incremental mode, per-test coverage, parallelism kullan.
Mutant, baska testleri de etkiler: Cozum: Testlerin bagimsiz oldugunu dogrula, shared state kullanma.
Flaky testler mutant'lari yanlis killed gosterebilir: Cozum: Once flaky testleri duzelt, sonra mutation test calistir.
Config dosyalarini mutate etmenin anlami yok: Cozum: mutate pattern'indan config, constants, types dosyalarini haric tut.
Bu skill su durumlarda aktive olur:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 17,152 | 14,286 | -17% | 1 | 1 | 0% | 3,008 | 5,810 | +93% | 0 | 0 | — |
case-02 | fail→fail | 13,544 | 9,467 | -30% | 1 | 1 | 0% | 2,573 | 4,780 | +86% | 0 | 0 | — |
case-03 | pass→pass | 11,053 | 6,457 | -42% | 1 | 1 | 0% | 1,957 | 4,227 | +116% | 0 | 0 | — |
case-04 | fail→pass | 11,092 | 8,844 | -20% | 1 | 1 | 0% | 2,053 | 4,773 | +132% | 0 | 0 | — |
case-05 | pass→pass | 7,402 | 6,686 | -10% | 1 | 1 | 0% | 1,284 | 4,254 | +231% | 0 | 0 | — |
case-06 | pass→pass | 8,467 | 6,705 | -21% | 1 | 1 | 0% | 1,518 | 4,264 | +181% | 0 | 0 | — |
case-07 | pass→pass | 11,657 | 8,788 | -25% | 1 | 1 | 0% | 1,929 | 4,801 | +149% | 0 | 0 | — |
case-08 | pass→pass | 14,240 | 12,602 | -12% | 1 | 1 | 0% | 2,491 | 5,314 | +113% | 0 | 0 | — |
case-09 | pass→pass | 10,381 | 4,981 | -52% | 1 | 1 | 0% | 1,655 | 3,926 | +137% | 0 | 0 | — |
case-15 | pass→pass | 10,687 | 8,866 | -17% | 1 | 1 | 0% | 1,799 | 4,568 | +154% | 0 | 0 | — |
case-10 | pass→pass | 13,164 | 9,446 | -28% | 1 | 1 | 0% | 2,044 | 4,652 | +128% | 0 | 0 | — |
case-11 | fail→pass | 17,425 | 18,750 | +8% | 1 | 1 | 0% | 2,967 | 6,359 | +114% | 0 | 0 | — |
case-12 | fail→pass | 14,520 | 15,451 | +6% | 1 | 1 | 0% | 2,932 | 5,952 | +103% | 0 | 0 | — |
case-13 | pass→pass | 8,151 | 4,570 | -44% | 1 | 1 | 0% | 1,501 | 3,786 | +152% | 0 | 0 | — |
case-14 | pass→pass | 11,788 | 7,272 | -38% | 1 | 1 | 0% | 2,217 | 4,351 | +96% | 0 | 0 | — |
case-16 | pass→pass | 11,112 | 12,122 | +9% | 1 | 1 | 0% | 1,886 | 5,047 | +168% | 0 | 0 | — |
case-17 | pass→pass | 8,644 | 6,257 | -28% | 1 | 1 | 0% | 1,610 | 4,093 | +154% | 0 | 0 | — |
case-18 | fail→pass | 11,283 | 6,115 | -46% | 1 | 1 | 0% | 1,800 | 4,015 | +123% | 0 | 0 | — |
case-19 | pass→pass | 4,584 | 3,715 | -19% | 1 | 1 | 0% | 844 | 3,654 | +333% | 0 | 0 | — |
case-20 | pass→pass | 4,773 | 4,363 | -9% | 1 | 1 | 0% | 859 | 3,821 | +345% | 0 | 0 | — |
case-21 | pass→pass | 9,378 | 6,496 | -31% | 1 | 1 | 0% | 1,973 | 4,460 | +126% | 0 | 0 | — |
case-22 | pass→pass | 6,458 | 5,277 | -18% | 1 | 1 | 0% | 1,299 | 4,109 | +216% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/29/2026 | +23% |
Other measured skills in the registry, with their headline benchmark lift.