---
name: mutation-testing
description: "Mutation testing ile test suite kalitesini olc. Stryker, mutmut, go-mutesting destegi."
---

# Mutation Testing

## Nedir?

Mutation testing, test suite'inin kalitesini olcen bir tekniktir. Kaynak kodda kucuk degisiklikler (mutasyonlar) yapilir ve testlerin bu degisiklikleri yakalayip yakalamadigina bakilir.

- **Mutant**: Kaynak kodda yapilan kucuk degisiklik
- **Killed**: Test suite mutant'i yakaladi (test fail etti)
- **Survived**: Test suite mutant'i yakalayamadi (testler hala geciyor)
- **Kill Ratio**: Killed / Total mutants (yuzde olarak)

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.

## Tool Setup

### Stryker (JavaScript / TypeScript)

```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:
```bash
npx stryker run
# Report: reports/mutation/mutation.html
```

### mutmut (Python)

```bash
pip 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
```

### go-mutesting (Go)

```bash
go 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 ./...
```

## Mutation Operatorleri

### Arithmetic Mutations
```
a + b  ->  a - b, a * b, a / b
a * b  ->  a / b, a + b
a++    ->  a--
```
Neyi test eder: Matematiksel hesaplamalarin dogrulugu

### Conditional Boundary Mutations
```
a > b   ->  a >= b
a < b   ->  a <= b
a >= b  ->  a > b
a <= b  ->  a < b
```
Neyi test eder: Boundary condition'lar, off-by-one hatalari

### Boolean Mutations
```
true    ->  false
a && b  ->  a || b
a || b  ->  a && b
!a      ->  a
```
Neyi test eder: Boolean logic, branch coverage

### Negation Mutations
```
if (condition)   ->  if (!condition)
while (x > 0)    ->  while (x <= 0)
```
Neyi test eder: Kontrol akisinin dogrulugu

### Return Value Mutations
```
return x       ->  return 0
return true    ->  return false
return "hello" ->  return ""
return obj     ->  return null
```
Neyi test eder: Return value assertion'lari

### String Mutations
```
"hello"  ->  ""
"hello"  ->  "Stryker was here!"
```
Neyi test eder: String handling, empty string kontrolu

### Statement Removal
```
doSomething();  ->  (removed)
x = calculate() ->  (removed)
```
Neyi test eder: Side effect'lerin test edilip edilmedigi

## Kill Ratio Hedefleri

| 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

## Survived Mutant Analizi

Bir mutant survive ettiyse su adimlari takip et:

### 1. Mutant'i Anla
```
Dosya: src/calculator.ts:15
Original:  if (balance > 0) { ... }
Mutant:    if (balance >= 0) { ... }
Durum:     SURVIVED
```

### 2. Neden Survive Etti?
- Hicbir test `balance === 0` durumunu test etmiyor
- Boundary condition icin test eksik

### 3. Test Yaz
```typescript
it('should handle zero balance', () => {
  const result = processBalance(0);
  expect(result).toBe('no_funds'); // Bu test mutant'i oldurur
});
```

### 4. Tekrar Calistir
```bash
npx stryker run --mutate "src/calculator.ts"
```

## Test Iyilestirme Pattern'leri

### Pattern 1: Boundary Testing
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));
```

### Pattern 2: Return Value Assertion
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
```

### Pattern 3: Boolean Logic
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));
```

### Pattern 4: Side Effect Testing
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');
```

### Pattern 5: Negation Testing
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'));
```

## CI/CD Entegrasyonu

### GitHub Actions

```yaml
name: 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
```

### GitLab CI

```yaml
mutation-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
```

### Incremental CI (PR'larda)

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 }}"
```

## Performance Optimization

### 1. Incremental Mutation Testing
Sadece degisen dosyalari mutate et:
```bash
# Stryker
npx stryker run --mutate "src/changed-file.ts"

# mutmut
mutmut run --paths-to-mutate src/changed_module/
```

### 2. Per-Test Coverage Analysis
Stryker'da `coverageAnalysis: 'perTest'` kullan. Her mutant sadece ilgili testlerle calistirilir.

### 3. Timeout Ayari
Sonsuz donguye giren mutant'lar icin makul timeout:
```javascript
timeoutMS: 60000,     // 60 saniye max
timeoutFactor: 1.5    // Normal surenin 1.5 kati
```

### 4. Concurrency
CPU sayisina gore paralel calistir:
```javascript
concurrency: 4  // veya os.cpus().length - 1
```

### 5. Incremental Mode (Stryker)
Onceki sonuclari cache'le:
```javascript
incremental: true,
incrementalFile: 'reports/stryker-incremental.json'
```

## Common Pitfalls

### 1. Equivalent Mutants
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.

### 2. Infinite Loop Mutants
`while (true)` veya `for(;;)` gibi durumlar:
Cozum: Timeout ayarini dogru yap, timeout mutant'larini "killed" say.

### 3. Cok Uzun Calisma Suresi
Buyuk codebase'lerde saatlerce surebilir:
Cozum: Incremental mode, per-test coverage, parallelism kullan.

### 4. Test Isolation Sorunlari
Mutant, baska testleri de etkiler:
Cozum: Testlerin bagimsiz oldugunu dogrula, shared state kullanma.

### 5. Flaky Test False Positives
Flaky testler mutant'lari yanlis killed gosterebilir:
Cozum: Once flaky testleri duzelt, sonra mutation test calistir.

### 6. Config Dosyalari / Constants
Config dosyalarini mutate etmenin anlami yok:
Cozum: `mutate` pattern'indan config, constants, types dosyalarini haric tut.

## Triggers

Bu skill su durumlarda aktive olur:
- "mutation test" dendiginde
- "test quality" soruldugunda
- "test effectiveness" degerlendirmesi istendiginde
- "kill ratio" soruldugunda
- "survived mutant" analizi gerektiginde
- Test suite guvenirligi sorgulandiginda
