# Getting Started with muttest

This guide walks you through your first mutation test run, from setup to interpreting results.

## Prerequisites

`muttest` works with R packages with `testthat` tests out of the box. If you’re not using a package structure, or not using `testthat`, see `?TestStrategy` for configuration options.

## Step 1 — Pick a source file to mutate

Start small. Choose one file from `R/` that contains meaningful logic — branching, arithmetic, comparisons. Avoid files that are mostly glue code or just call other functions.

## Step 2 — Define a test plan

A test plan describes what to mutate and which mutations to apply:

    library(muttest)

    plan <- muttest_plan(
      source_files = "R/is_adult.R",
      mutators = comparison_operators()
    )

`comparison_operators()` is a preset that generates mutants by swapping each comparison operator for related alternatives. For `>=` it produces two mutants: `>=` → `>` and `>=` → `<=`.

## Step 3 — Run the tests

    muttest::muttest(plan, "tests/testthat")

## Step 4 — Read the output and improve your tests

Each column in the progress table means:

Column | Meaning
--- | ---
K | Killed — mutants your tests caught
S | Survived — mutants your tests missed
E | Errors — mutated code errored; counted as killed
N | No coverage — no test exercised the mutant; excluded from the score
T | Total mutants for this mutator/file combination
% | Mutation score for this row

The mutation score is `(Killed + Errors) / (Killed + Survived + Errors) × 100%` — errored mutants count as detected, no-coverage mutants are excluded. A `✔` row means at least one mutant was killed; an `x` row means all mutants survived.

Here is a complete example showing a weak test, the live output, and the fix:

### Example: `is_adult` — Missing Boundary Value

A test suite that checks only values clearly on one side of a threshold will let boundary-shift comparison mutants survive. The surviving mutant names the exact input your tests have never exercised. Adding a test at that precise boundary kills it.

#### The function

    is_adult <- function(age) {
      age >= 18
    }

`is_adult` returns `TRUE` when age is 18 or older, using `>=`. Swapping to `>` would incorrectly classify an 18-year-old as a minor.

#### Weak test

    test_that("is_adult returns TRUE for adults", {
      expect_true(is_adult(25))
    })

    test_that("is_adult returns FALSE for minors", {
      expect_false(is_adult(10))
    })

The tests check age 25 (clearly adult) and age 10 (clearly minor). Both inputs return the same result whether the operator is `>=` or `>`, so the tests cannot tell the operators apart.

Mutation testing output:

    ℹ Mutation Testing
      |   K |   S |   N |   E |   T |   % | Mutator  | File
    ✔ |   1 |   0 |   0 |   0 |   1 | 100 | >= → <=  | is_adult.R
    x |   1 |   1 |   0 |   0 |   2 |  50 | >= → >   | is_adult.R

    ── Results ─────────────────────────────────────────────────────────────────────
    [ KILLED 1 | SURVIVED 1 | NO COVERAGE 0 | ERRORS 0 | TOTAL 2 | SCORE 50.0% ]

`>= → >` survives. Changing `age >= 18` to `age > 18` only affects one input — `age = 18` exactly. The tests never pass 18, so the function behaves identically for 25 and 10 under both operators.

#### The fix

    test_that("is_adult returns TRUE for adults", {
      expect_true(is_adult(25))
    })

    test_that("is_adult returns FALSE for minors", {
      expect_false(is_adult(10))
    })

    test_that("is_adult returns TRUE at the boundary age", {
      expect_true(is_adult(18))
    })

Add a test at the boundary value 18. With `>= 18`, the condition is `TRUE` and the function returns `TRUE`. With `> 18`, it is `FALSE` and returns `FALSE`. This difference kills the mutant.

After the fix:

    ℹ Mutation Testing
      |   K |   S |   N |   E |   T |   % | Mutator  | File
    ✔ |   1 |   0 |   0 |   0 |   1 | 100 | >= → <=  | is_adult.R
    ✔ |   2 |   0 |   0 |   0 |   2 | 100 | >= → >   | is_adult.R

    ── Results ─────────────────────────────────────────────────────────────────────
    [ KILLED 2 | SURVIVED 0 | NO COVERAGE 0 | ERRORS 0 | TOTAL 2 | SCORE 100.0% ]

> When a comparison mutant survives, find the boundary value implied by the operator and add a test that passes exactly that value.

## The feedback loop

1. Write tests
2. Run muttest
3. Find survivors
4. Strengthen tests
5. Repeat

Start with one file. Aim for a meaningful score improvement each iteration rather than chasing 100% immediately. A score of 80%+ on critical business logic can be a reasonable target to start from.

## Next steps

- What is Mutation Testing? — conceptual background and when mutation testing pays off most
- Mutator Reference — all available mutators and how to pick the right ones
- CI Integration — run mutation tests automatically on every push