ts-mutate
v1.0.2
Published
Minimal TypeScript mutation-testing wrapper for Stryker
Maintainers
Readme
ts-mutate
ts-mutate is a minimal TypeScript mutation-testing wrapper around
StrykerJS that adds a small API inspired by the
practical targeting surface of slither-mutate:
- class-based targeting
- function and method selector targeting
- mutator category selection
- strict post-run verification that Stryker only mutated the requested scope
The wrapper resolves TypeScript symbols with ts-morph, generates a temporary
Stryker config with narrow mutate ranges, runs Stryker's command runner, and
optionally validates the JSON mutation report.
Status
MVP / experimental. The mutator registry is pinned to StrykerJS 9.6.1 and is
intended for focused mutation runs, not as a replacement for Stryker's full CLI.
Requirements
- Node.js 22 or newer
- npm
- A TypeScript project with a valid
tsconfig.json - A test command that succeeds before mutation testing
Install from source
git clone https://github.com/stevennevins/ts-mutate.git
cd ts-mutate
npm ci --ignore-scripts
npm run buildRun the local CLI:
node dist/cli.js --helpOr link it locally:
npm linkThen use:
ts-mutate <project> [options]CLI usage
ts-mutate <project> \
--class-names <names> \
--target-functions <selectors> \
--mutators <categories> \
--test-cmd <command> \
--output-dir <path> \
--strictAt least one of --class-names or --target-functions is required.
--test-cmd is required unless you are only inspecting validation errors before
Stryker runs.
Options
| Option | Required | Description |
| --- | --- | --- |
| <project> | yes | Project root to mutate. |
| --class-names <names> | no | Comma-separated class names to target. |
| --target-functions <selectors> | no | Comma-separated function/method selectors. |
| --mutators <categories> | no | Comma-separated mutator categories. Defaults to all. |
| --test-cmd <command> | yes | Command passed to Stryker's command runner. |
| --config <path> | no | Base Stryker config to merge. |
| --tsconfig <path> | no | TypeScript config. Defaults to <project>/tsconfig.json. |
| --output-dir <path> | no | Output directory. Defaults to <project>/reports/mutation. |
| --dry-run | no | Resolve targets and write config without running Stryker. |
| --allow-multiple | no | Allow one selector to resolve to multiple symbols. |
| --strict | no | Verify final mutants are in-scope and use allowed mutators. |
Examples
Target classes
ts-mutate . \
--class-names AuthService,UserService \
--test-cmd "npm test" \
--strictTarget a method
ts-mutate . \
--target-functions "AuthService.verifySignature" \
--test-cmd "npm test" \
--strictTarget by parameter signature
ts-mutate . \
--target-functions "verifySignature(string,string),AuthService.canWithdraw(number)" \
--test-cmd "npm test" \
--strictRestrict mutator categories
ts-mutate . \
--target-functions "AuthService.verify" \
--mutators conditionals,logical,boolean \
--test-cmd "npm test" \
--strictDry-run target resolution
ts-mutate . \
--class-names AuthService \
--test-cmd "npm test" \
--dry-runThe command prints JSON including the generated Stryker config path and resolved source ranges.
Supported target selectors
| Selector form | Meaning |
| --- | --- |
| functionName | Top-level function declaration, function expression, or const arrow function. |
| functionName(type,type) | Function with matching normalized parameter types. |
| ClassName.methodName | Method inside a class. |
| ClassName.methodName(type,type) | Class method with matching normalized parameter types. |
Class targeting is provided separately through --class-names.
Out of scope for the MVP: raw line ranges, namespace selectors,
file.ts:functionName, decorator-specific selectors, and PR-diff targeting.
Mutator categories
ts-mutate translates category inclusion into Stryker's
mutator.excludedMutations setting.
Supported categories:
allconditionalslogicalarithmeticbooleanstringarrayobjectfunction-calltypescript
The registry is version-pinned in src/mutators.ts for StrykerJS 9.6.1.
Unknown report mutators fail under --strict.
Strict verification
With --strict, ts-mutate reads Stryker's JSON report after the run and checks
that every non-ignored mutant:
- has a source location,
- is inside one of the resolved target ranges,
- uses a known mutator, and
- uses a mutator enabled by the requested categories.
Strict mode fails if the JSON report is missing, a mutant is out of range, or a mutator is unknown/disallowed. Surviving mutants are not a wrapper failure; they mean the target project's tests did not kill those mutants.
Generated Stryker config
The generated config uses Stryker's command runner by default:
{
"testRunner": "command",
"commandRunner": {
"command": "npm test"
},
"coverageAnalysis": "off",
"incremental": true,
"checkers": ["typescript"],
"reporters": ["clear-text", "json"]
}coverageAnalysis is intentionally off because arbitrary command-runner test
commands cannot safely use per-test coverage.
Programmatic API
import { runTsMutate } from "ts-mutate";
const result = await runTsMutate({
project: ".",
targetFunctions: ["AuthService.verifySignature(string,string)"],
mutators: ["conditionals", "logical", "boolean"],
testCmd: "npm test",
strict: true,
});
console.log(result.exitCode, result.verification);Key exported types include:
TsMutateOptionsMutatorCategoryResolvedTargetVerificationResultTsMutateResult
Development
npm ci --ignore-scripts
npm test -- --run
npm run buildCI runs the same install, test, and build steps on Node.js 22.x and 24.x.
Limitations
- Uses Stryker's command runner only.
- Does not implement a custom mutation engine or native Stryker plugin.
- Does not support raw source ranges or PR-diff targeting.
- Mutator category names depend on the pinned StrykerJS version.
- The target project's baseline test command must pass before Stryker can run.
License
ISC
