Skip to content

About

Mutation testing with StrykerJS + TypeScript + Vitest - proves test-suite quality (not just coverage) against a realistic pricing/discount engine. ~98% mutation score with an 80% CI break gate.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Mutation Testing with StrykerJS

CI TypeScript Stryker Vitest

A focused demonstration of mutation testing — the technique that measures how good your tests are, not just how much code they touch. It uses StrykerJS to mutate a realistic TypeScript pricing/discount library and re-runs the Vitest suite against every mutant.

Why mutation testing?

Line/branch coverage tells you which code ran during tests — not whether your assertions would actually catch a bug. Mutation testing closes that gap:

  1. Stryker introduces small faults ("mutants") into the source — flipping >= to >, + to -, && to ||, removing a branch, etc.
  2. It re-runs the test suite against each mutant.
  3. If a test fails, the mutant is killed (good — your tests caught the change). If all tests still pass, the mutant survived (a blind spot in your suite).

The mutation score = killed / total mutants. A high score is far stronger evidence of suite quality than a high coverage percentage.

What's inside

  • src/pricing.ts — a non-trivial domain library (subtotal, tiered membership discounts with a promo-code cap, discounted-subtotal-based free shipping, tax, and a full breakdown). It is deliberately rich in the operators and boundaries mutation testing targets.
  • src/pricing.test.ts — a thorough Vitest suite written to kill mutants: boundary cases (at/just-below/just-above thresholds), each branch, the discount cap, and integration breakdowns.
  • stryker.config.json — StrykerJS config with a break threshold of 80%, so CI fails if the suite ever weakens.

Getting started

# 1. Install dependencies
npm install

# 2. Run the unit tests
npm test

# 3. Run mutation testing (produces reports/mutation/index.html)
npm run test:mutation

Scripts

Command What it does
npm test Run the Vitest unit suite once
npm run test:watch Vitest in watch mode
npm run coverage Unit tests with a V8 coverage report
npm run test:mutation Run StrykerJS mutation testing + HTML report
npm run typecheck Type-check with tsc --noEmit

Reading the results

After npm run test:mutation, Stryker prints a summary and writes an interactive report to reports/mutation/index.html. Surviving mutants are highlighted in the source so you can see exactly which change your tests failed to detect — then add a test to kill it. This suite scores ~98%, well above the 80% break threshold.

On the one surviving mutant (equivalent mutant)

The report shows a single survivor: rate > MAX_DISCOUNT_RATE mutated to rate >= MAX_DISCOUNT_RATE. This is an equivalent mutant — it cannot be killed by any test. At the only boundary value (rate === 0.25), clamping to 0.25 is a no-op, so > and >= produce identical output. Recognising equivalent mutants (and not chasing an artificial 100%) is part of using mutation testing well.

Notes on scope

  • Mutation testing is CPU-intensive because the suite runs once per mutant; it is typically run on focused, high-value modules (like this pricing engine) rather than an entire codebase on every commit.
  • The library is pure and dependency-free by design, which keeps mutants fast to evaluate and the example easy to follow.

Author

Darrius Jones (@Djones-qa · LinkedIn)

License

MIT © Darrius Jones

About

Mutation testing with StrykerJS + TypeScript + Vitest - proves test-suite quality (not just coverage) against a realistic pricing/discount engine. ~98% mutation score with an 80% CI break gate.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages