Contents

ericodx/swift-mutation-testing

**Measure and improve test effectiveness in Swift codebases using mutation testing.**

Why

Traditional test coverage does not guarantee that tests catch real bugs.

Mutation testing introduces controlled changes to your code to verify that your tests fail when behavior changes. Surviving mutations indicate gaps in test effectiveness.

Features

  • Mutation testing for Xcode and SPM projects
  • Supports both XCTest and Swift Testing frameworks
  • 7 mutation operators (relational, boolean, logical, arithmetic, negate conditional, swap ternary, remove side effects)
  • Schematization — builds once, tests all mutants via runtime switch
  • Parallel test execution with configurable concurrency
  • SHA256-based result caching across runs
  • Multiple report formats: text, JSON (Stryker-compatible), HTML, SonarQube
  • Simulator pool management for iOS/tvOS/watchOS targets
  • Per-scope mutation suppression via @SwiftMutationTestingDisabled
  • Configurable via YAML or CLI flags
  • CI/CD ready with caching support

Install

brew tap ericodx/homebrew-tools
brew install swift-mutation-testing

Other installation methods — pre-built binary, build from source — are covered in the Installation Guide.

Quick start

# Generate a config file (auto-detects project type, scheme, destination, and test targets)
swift-mutation-testing init

# Run mutation testing
swift-mutation-testing

# Run on an SPM package (no scheme or destination needed)
swift-mutation-testing /path/to/my-package

Example output:

 Discovery: 147 mutants (143 schematizable, 4 incompatible) in 2.3s

Building for testing...
 Built in 18.4s
 3 simulators ready

Testing mutants...
 1/147  RelationalOperatorReplacement  Validator.swift:18
 2/147  NegateConditional              Validator.swift:34
 3/147  BooleanLiteralReplacement      FeatureFlags.swift:9

Results by file:
  Sources/Validator.swift      score: 72.4%   killed: 21   survived: 8   timeout: 0   unviable: 0
  Sources/FeatureFlags.swift   score: 100.0%  killed: 6    survived: 0   timeout: 0   unviable: 0

Survived mutants:
  Sources/Validator.swift:34:5   NegateConditional

Overall mutation score: 83.2%
Killed: 122 / Survived: 21 / Timeouts: 0 / Unviable: 4 / NoCoverage: 0
Total duration: 312.7s

Configuration

Drop a .swift-mutation-testing.yml in the project root:

Xcode project:

scheme: MyApp
destination: platform=iOS Simulator,name=iPhone 16
# testTarget: MyAppTests
# timeout: 120
# concurrency: 4

SPM package (scheme and destination are not needed):

# testTarget: MyPackageTests
# timeout: 30
# concurrency: 4

Mutation operators (both project types — all active by default):

mutators:
  - name: RelationalOperatorReplacement
    active: true
  - name: BooleanLiteralReplacement
    active: true
  - name: LogicalOperatorReplacement
    active: true
  - name: ArithmeticOperatorReplacement
    active: true
  - name: NegateConditional
    active: true
  - name: SwapTernary
    active: true
  - name: RemoveSideEffects
    active: true

Full reference in the Usage & Configuration Guide.

Documentation

| Document | Description | |---|---| | Installation | Homebrew, pre-built binary, build from source | | Usage & Configuration | CLI options, YAML config, output formats, CI integration | | Mutation Results | What each result means and when it occurs; schematizable vs incompatible mutants | | Architecture | Pipeline design, module map, schematization, execution model | | Codebase Reference | Every type, protocol, and stage documented | | Stryker Compatibility | JSON report format compatibility with the Stryker schema |

Package Metadata

Repository: ericodx/swift-mutation-testing

Default branch: main

README: README.md