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-testingOther 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-packageExample 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.7sConfiguration
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: 4SPM package (scheme and destination are not needed):
# testTarget: MyPackageTests
# timeout: 30
# concurrency: 4Mutation 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: trueFull 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