The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
This guide walks you through your first mutation test run, from setup to interpreting results.
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.
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.
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 >= → <=.
Each column in the progress table means:
| Column | Meaning |
|---|---|
| K | Killed — mutants your tests caught |
| S | Survived — mutants your tests missed |
| E | Errors — mutants that caused unexpected errors |
| T | Total mutants for this mutator/file combination |
| % | Mutation score for this row |
The mutation score is
Killed / Total × 100%. 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:
is_adult — Missing Boundary ValueA 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.
is_adult returns TRUE when age is 18 or
older, using >=. Swapping to > would
incorrectly classify an 18-year-old as a minor.
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 | E | T | % | Mutator | File
✔ | 1 | 0 | 0 | 1 | 100 | >= → <= | is_adult.R
x | 1 | 1 | 0 | 2 | 50 | >= → > | is_adult.R
── Results ─────────────────────────────────────────────────────────────────────
[ KILLED 1 | SURVIVED 1 | 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.
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 | E | T | % | Mutator | File
✔ | 1 | 0 | 0 | 1 | 100 | >= → <= | is_adult.R
✔ | 2 | 0 | 0 | 2 | 100 | >= → > | is_adult.R
── Results ─────────────────────────────────────────────────────────────────────
[ KILLED 2 | SURVIVED 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.
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.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.