一键导入
using-clj-mutate
Use when mutation-testing Clojure code, assessing test quality beyond coverage, or investigating surviving mutations to strengthen specs
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when mutation-testing Clojure code, assessing test quality beyond coverage, or investigating surviving mutations to strengthen specs
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | using-clj-mutate |
| description | Use when mutation-testing Clojure code, assessing test quality beyond coverage, or investigating surviving mutations to strengthen specs |
Mutation testing for Clojure. Mutates source code systematically, runs your specs against each mutant, and reports which mutations survived (tests didn't catch) vs were killed (tests caught). Surviving mutations reveal gaps in your test suite that line coverage alone cannot detect.
clj-mutate auto-detects whether your project uses bb.edn (Babashka) or deps.edn (JVM Clojure) and picks the default spec runner accordingly.
Add a :mutate alias to your project's deps.edn:
:mutate {:main-opts ["-m" "clj-mutate.core"]
:extra-deps {clj-mutate/clj-mutate {:local/root "/path/to/clj-mutate"}
org.clojure/tools.reader {:mvn/version "1.4.2"}}
:extra-paths ["spec"]}
Requires Speclj as test runner with a :spec alias that runs all specs.
Optional coverage integration (skips mutations on uncovered lines):
:cov {:main-opts ["-m" "speclj.cloverage" "--" "-p" "src" "-s" "spec" "--lcov"]
:extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}
speclj/speclj {:mvn/version "3.12.2"}}
:extra-paths ["spec"]}
Add mutate and spec tasks to your project's bb.edn:
{:paths ["src" "spec"]
:deps {clj-mutate/clj-mutate {:local/root "/path/to/clj-mutate"}
org.clojure/tools.reader {:mvn/version "1.4.2"}
speclj/speclj {:mvn/version "3.12.2"}}
:tasks {spec {:doc "Run all specs"
:requires ([speclj.main :as speclj])
:task (speclj/-main "-c")}
mutate {:doc "Run mutation testing"
:requires ([clj-mutate.core :as mutate])
:task (apply mutate/-main *command-line-args*)}}}
Use speclj 3.12.2+ under Babashka so failed specs propagate non-zero exit codes correctly.
Cloverage is not available under Babashka; coverage-guided filtering is skipped unless target/coverage/lcov.info already exists.
# deps.edn projects
# If the file has a footer manifest, this defaults to changed top-level forms only.
clj -M:mutate src/myapp/foo.cljc
# bb.edn projects
bb mutate src/myapp/foo.cljc
# Scan mutation counts without running coverage or specs
clj -M:mutate src/myapp/foo.cljc --scan
# Rewrite the embedded manifest without running coverage or mutations
clj -M:mutate src/myapp/foo.cljc --update-manifest
# Retest only specific lines (e.g. survivors from previous run)
clj -M:mutate src/myapp/foo.cljc --lines 45,67,89
# Retest only top-level forms changed since the last successful mutation run
clj -M:mutate src/myapp/foo.cljc --since-last-run
# Override differential mode and run all covered mutations
clj -M:mutate src/myapp/foo.cljc --mutate-all
# Reuse existing LCOV data without refreshing coverage
clj -M:mutate src/myapp/foo.cljc --reuse-lcov
The tool automatically:
bb.edn or deps.edn) and uses the appropriate default spec runner:tested-at and top-level form hashes on successful runslcov.info is missing or stalelcov.info if present, otherwise tests all lines--reuse-lcov:no-mutate by default to avoid nested mutation runs inside mutation workersUse --scan when you want a fast module-size signal without paying for coverage or test execution. It reports total mutation sites, changed mutation sites, and the usual warning threshold output.
Use --update-manifest when you want to accept the current file contents as the new differential baseline without paying for coverage or mutation execution.
Use --reuse-lcov when coverage has already been generated and you want to skip an expensive LCOV refresh. The run will warn that covered/uncovered classification may be stale. If target/coverage/lcov.info is missing, the run prints a clear error and exits.
| Category | Mutations |
|---|---|
| Arithmetic | + <-> -, * -> /, inc <-> dec |
| Comparison | > <-> >=, < <-> <= |
| Equality | = <-> not= |
| Boolean | true <-> false |
| Conditional | if <-> if-not, when <-> when-not |
| Constant | 0 <-> 1 |
| Result | Meaning | Action |
|---|---|---|
| Killed | Test caught the mutation | Tests are strong here |
| Survived | Tests passed with mutant code | Write a spec that would fail with the mutation |
| Timeout | Mutation caused infinite loop | Treated as killed (mutation had observable impact) |
| Equivalent | Mutation can't change behavior | Auto-suppressed; no action needed |
Known-equivalent mutations are auto-suppressed to reduce false survivors:
</<=/>/>= mutations on (rand) comparisons= mutations in rand-nth single-element guardsrand-nth [...] pools> -> >= at subvec boundariesclj -M:mutate ... or bb mutate ...)--linesFor a batch of files, the first mutation run can generate coverage and later runs can use --reuse-lcov to avoid repeating LCOV refresh, if you accept the stale-coverage tradeoff.
For incremental work on an already-mutated file, the default run is already differential. You can still be explicit:
clj -M:mutate src/myapp/foo.cljc --since-last-run
This compares current top-level forms with the footer manifest from the last successful mutation run and tests only forms whose hashes changed.
Before baseline and worker execution, differential runs also print:
To force a full rerun on a file with a manifest:
clj -M:mutate src/myapp/foo.cljc --mutate-all
:spec alias (deps.edn) or spec task (bb.edn) runs all specs under spec/run-mutation-testing as :no-mutate, or override the worker command with --test-command