This vignette explains how to run mutator on a package in GitHub Actions using the reusable workflow that ships with the package, how to gate a pull request on a minimum mutation score, and how to publish a badge.
mutator provides a reusable workflow at
PRL-PRG/mutator/.github/workflows/mutation-testing.yaml. A
repository adopts it with a short caller workflow rather than by copying
any script. The workflow checks out your package, installs it together
with mutator and its dependencies, runs mutate_package(),
uploads a results artifact, and writes a Markdown summary to the run’s
Summary tab.
Add .github/workflows/mutation-testing.yaml to your
repository:
on:
pull_request:
push:
branches: [main, master]
workflow_dispatch:
name: mutation-testing
jobs:
mutation:
uses: PRL-PRG/mutator/.github/workflows/[email protected]
with:
target-margin: "0.10"Pin to a released tag such as @v0.2.0. The reusable
workflow lives in the mutator package repository and is versioned with
the package, so the tag you pin matches a mutator release. Bump the tag
in your caller when you want to move to a newer release. You can also
pin to a branch (for example @main) to track the latest
changes, at the cost of reproducibility.
All inputs are optional.
| Input | Default | Meaning |
|---|---|---|
target-margin |
"0.10" |
Sample mutants to this plus or minus CI half-width, as a proportion. Empty tests every mutant. |
max-mutants |
"" |
Fixed cap on sampled mutants. Takes precedence over
target-margin. |
fail-under |
"" |
Fail the job when the score is below this percentage. |
show-confidence-interval |
true |
Show the confidence interval with a sampled mutation score in the badge and job summary. |
cores |
"0" |
Parallel workers. "0" auto-detects. |
timeout-seconds |
"" |
Per-mutant timeout. Empty derives it from the baseline run. |
isolate |
true |
Deep-copy src/ and tests/ per mutant, for
non-hermetic suites. |
coverage-guided |
true |
Run only the tests covering each mutated line. Disabled automatically, with a warning, for installed-tests packages. |
coverage-backend |
record_tests |
How coverage is attributed to tests. "per_file" gives
exact file-level attribution (no helper-file fallback), at the cost of
relying on covr internals. |
exclude-files |
"" |
Glob patterns of R/ files to skip. |
badge-label |
mutator |
Label shown on the shields.io badge. |
deploy-badge |
false |
Publish a badge. Requires contents: write. |
badge-branch |
gh-pages |
Branch the badge JSON is deployed to. |
mutator-spec |
github::PRL-PRG/mutator |
pak spec used to install mutator, for example
any::mutator once it is on CRAN. |
install-imputesrcref |
true |
Install the optional imputesrcref package to refine mutant locations. |
r-version |
release |
R version passed to setup-r. |
Set fail-under to the percentage below which the job
should fail. The workflow computes the mutation score, and if it is
below the threshold the job exits non-zero, which blocks the pull
request:
jobs:
mutation:
uses: PRL-PRG/mutator/.github/workflows/[email protected]
with:
fail-under: "75"
target-margin: "0.05"Two points make a threshold gate reliable:
target-margin (for example "0.05") or a large
max-mutants when you gate on fail-under, so
the estimate does not cross the threshold by chance. The half-width you
choose is roughly how far the reported score can sit from the true
score.fail-under at, or slightly
below, your current score, then raise it as you add tests. This locks in
progress without failing the build on the day you adopt it.Leave fail-under empty to report the score on every run
without failing.
The measured score, its confidence interval, and the killed, survived, and hanged counts appear in the run’s Summary tab and in the uploaded artifact regardless of whether a threshold is set.
Set deploy-badge: true and grant the caller job
contents: write. The workflow writes a shields.io endpoint JSON file and
deploys it to gh-pages (or badge-branch):
jobs:
mutation:
permissions:
contents: write
uses: PRL-PRG/mutator/.github/workflows/[email protected]
with:
deploy-badge: trueReference the badge in your README, replacing
OWNER/REPO:
This reads the JSON straight from badge-branch via
raw.githubusercontent.com, so no GitHub Pages setup is
needed. If you instead fetch it through a pkgdown Pages site
(https://OWNER.github.io/REPO/mutation-score.json), GitHub
Pages must be activated and serving from badge-branch, or
the badge will not appear.
The badge is labelled mutator; its message shows the
score and, when the run sampled mutants, a compact asymmetric confidence
interval such as 83.2% -5.2/+4.7% (95% CI). The badge
colour tracks the score: green above 80%, yellow above 60%, orange above
40%, and red below.
Set show-confidence-interval: false to show only the
mutation score in both the badge and the job summary. The confidence
interval remains part of the uploaded mutation results.
The workflow reads GITHUB_TOKEN to install packages from
GitHub, which is provided automatically. You only need to set
permissions: contents: write on the caller job when
deploy-badge is true, because publishing the badge pushes
to a branch. Pull request runs never deploy the badge.
If your package depends on packages that are not on CRAN, pass them
through extra-packages as pak specs:
Mutation testing runs the test suite once per mutant, so a large package can take a while. To keep CI fast:
max-mutants, or let
target-margin size the sample for you.coverage-guided is on by default and runs only the
tests that cover each mutated line. It applies to packages tested with
testthat; for installed-tests packages the workflow warns and runs the
full suite.exclude-files to skip generated or vendored files
under R/.cores on a larger runner, or leave it at
"0" to use every core.See the Configuration article for how these options behave in detail.