# Python Mutation Testing Example
This example demonstrates how to use PMAT for mutation testing on a Python project.
## Overview
Mutation testing is a technique to evaluate the quality of your test suite by introducing small changes (mutations) to your code and checking if your tests detect them.
**Mutation Score** = (Killed Mutants / Total Valid Mutants) × 100%
- **Killed**: Test suite detected the mutation (good!)
- **Survived**: Test suite did not detect the mutation (test gap!)
- **CompileError**: Mutation caused invalid syntax
- **Timeout**: Mutation caused infinite loop
## Project Structure
```
python-mutation-testing/
├── src/
│ └── calculator.py # Calculator module with 8 functions
├── tests/
│ └── test_calculator.py # Comprehensive test suite (24 tests)
├── requirements.txt # Python dependencies
└── README.md # This file
```
## Getting Started
### 1. Install Dependencies
First, install the required Python packages:
```bash
cd examples/python-mutation-testing
pip install -r requirements.txt
```
### 2. Run Tests
Verify that all tests pass:
```bash
pytest tests/test_calculator.py -v
```
You should see:
```
===================== test session starts =====================
collected 24 tests
tests/test_calculator.py::TestArithmeticOperations::test_add PASSED
tests/test_calculator.py::TestArithmeticOperations::test_subtract PASSED
tests/test_calculator.py::TestArithmeticOperations::test_multiply PASSED
tests/test_calculator.py::TestArithmeticOperations::test_divide PASSED
tests/test_calculator.py::TestLogicalOperations::test_is_even PASSED
tests/test_calculator.py::TestLogicalOperations::test_max_value PASSED
tests/test_calculator.py::TestComplexOperations::test_factorial PASSED
tests/test_calculator.py::TestComplexOperations::test_factorial_negative PASSED
tests/test_calculator.py::TestComplexOperations::test_is_prime PASSED
... (15 more tests)
===================== 24 passed in 0.05s =====================
```
### 3. Run Mutation Testing
Use PMAT to perform mutation testing on the calculator module:
```bash
# Install pmat if not already installed
cargo install pmat
# Run mutation testing on the calculator module
pmat mutate --target src/calculator.py
```
### 4. Analyze Results
PMAT will generate mutants and run your tests against each one. Example output:
```
Mutation Testing Results
========================
Total Mutants: 52
Killed: 47 (90.4%)
Survived: 3 (5.8%)
Compile Errors: 2 (3.8%)
Timeouts: 0 (0.0%)
Mutation Score: 90.4%
```
### 5. Review Survived Mutants (Test Gaps)
Use `--failures-only` to see only survived mutants:
```bash
pmat mutate --target src/calculator.py --failures-only
```
This shows which mutations your tests failed to catch, indicating gaps in test coverage.
## Example: Detecting Test Gaps
### Original Code
```python
def max_value(a: int, b: int) -> int:
"""Calculate the maximum of two numbers."""
if a > b: # Mutation: change > to >=
return a
else:
return b
```
### Mutation
```python
def max_value(a: int, b: int) -> int:
"""Calculate the maximum of two numbers."""
if a >= b: # Mutated: > changed to >=
return a
else:
return b
```
### Test That Catches It
```python
def test_max_value(self):
"""Test maximum value calculation."""
assert max_value(5, 3) == 5 # Would pass with either > or >=
assert max_value(2, 8) == 8 # Would pass with either > or >=
assert max_value(4, 4) == 4 # CRITICAL: Catches the >= mutation!
```
Without the `max_value(4, 4)` test case, the `> to >=` mutation would **survive**, indicating a test gap.
## Output Formats
### Text (Default)
```bash
pmat mutate --target src/calculator.py
```
Color-coded terminal output with summary and individual mutant details.
### JSON
```bash
pmat mutate --target src/calculator.py --output-format json > results.json
```
Machine-readable JSON for CI/CD integration.
### Markdown
```bash
pmat mutate --target src/calculator.py --output-format markdown > results.md
```
Human-readable report with tables and statistics.
## Advanced Usage
### Control Concurrency
```bash
# Use 4 parallel jobs
pmat mutate --target src/calculator.py --jobs 4
```
### Set Timeout
```bash
# Timeout individual tests after 10 seconds
pmat mutate --target src/calculator.py --timeout 10
```
### Enforce Mutation Score Threshold
```bash
# Fail if mutation score is below 85%
pmat mutate --target src/calculator.py --threshold 85
```
This will exit with code 1 if the mutation score is below 85%, useful for CI/CD quality gates.
## CI/CD Integration
### GitHub Actions
Create `.github/workflows/mutation-testing.yml`:
```yaml
name: Mutation Testing
on: [push, pull_request]
jobs:
mutation-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r examples/python-mutation-testing/requirements.txt
- name: Install Rust and pmat
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
cargo install pmat
- name: Run mutation tests
run: |
cd examples/python-mutation-testing
pmat mutate --target src/calculator.py --failures-only --threshold 80
```
### GitLab CI
Add to `.gitlab-ci.yml`:
```yaml
mutation-testing:
image: python:3.11
stage: test
before_script:
- pip install -r examples/python-mutation-testing/requirements.txt
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
- source $HOME/.cargo/env
- cargo install pmat
script:
- cd examples/python-mutation-testing
- pmat mutate --target src/calculator.py --output-format json > mutation_results.json
artifacts:
reports:
junit: mutation_results.json
```
## Best Practices
1. **Run mutation testing regularly** - Integrate into CI/CD pipeline
2. **Set reasonable thresholds** - 80-90% mutation score is excellent
3. **Focus on critical code** - Test business logic thoroughly
4. **Review survived mutants** - Each one represents a potential bug
5. **Use `--failures-only`** - Reduces noise in large codebases
6. **Test edge cases** - Boundary values catch more mutations
7. **Combine with coverage** - High code coverage + high mutation score = robust tests
## Understanding Mutation Operators
PMAT applies various mutation operators to Python code:
### Arithmetic Operators
- `+` → `-`, `*`, `/`, `//`, `%`
- `-` → `+`, `*`, `/`, `//`, `%`
- `*` → `+`, `-`, `/`, `//`, `%`
- `/` → `+`, `-`, `*`, `//`, `%`
- `//` → `+`, `-`, `*`, `/`, `%`
- `%` → `+`, `-`, `*`, `/`, `//`
### Comparison Operators
- `==` → `!=`, `<`, `>`, `<=`, `>=`
- `<` → `<=`, `>`, `>=`, `==`, `!=`
- `>` → `>=`, `<`, `<=`, `==`, `!=`
- `<=` → `<`, `>=`, `!=`
- `>=` → `>`, `<=`, `!=`
- `!=` → `==`, `<`, `>`, `<=`, `>=`
### Boolean Operators
- `and` → `or`
- `or` → `and`
- `not` → (removed)
### Boundary Values
- `<` → `<=`
- `>` → `>=`
- `0` → `1`, `-1`
- `range(n)` → `range(n+1)`, `range(n-1)`
### Return Values
- `return x` → `return None`, `return 0`, `return -1`
- `return True` → `return False`
- `return False` → `return True`
## Example Results
```
╔═══════════════════════════════════════════╗
║ Mutation Testing Summary ║
╠═══════════════════════════════════════════╣
║ Total Mutants: 52 ║
║ Killed: 47 (90.4%) ║
║ Survived: 3 (5.8%) ║
║ Compile Errors: 2 (3.8%) ║
║ Timeouts: 0 (0.0%) ║
╠═══════════════════════════════════════════╣
║ Mutation Score: 90.4% ✓ ║
╚═══════════════════════════════════════════╝
```
## Running Tests Manually
### With pytest
```bash
# Run all tests
pytest tests/
# Run with verbose output
pytest tests/ -v
# Run with coverage report
pytest tests/ --cov=src --cov-report=html
```
### With unittest (alternative)
```bash
python -m pytest tests/test_calculator.py
```
## Troubleshooting
### Import Errors
If you get `ModuleNotFoundError`, ensure you're running tests from the project root:
```bash
cd examples/python-mutation-testing
python -m pytest tests/
```
Or use the `PYTHONPATH` environment variable:
```bash
export PYTHONPATH="${PYTHONPATH}:$(pwd)/src"
pytest tests/
```
### Mutation Testing Takes Too Long
Use the `--timeout` flag to limit test execution time:
```bash
pmat mutate --target src/calculator.py --timeout 5
```
Or reduce concurrency if tests are CPU-bound:
```bash
pmat mutate --target src/calculator.py --jobs 1
```
## Resources
- **PMAT Repository**: https://github.com/paiml/paiml-mcp-agent-toolkit
- **Crates.io**: https://crates.io/crates/pmat
- **Documentation**: `server/README.md`
- **Mutation Testing Paper**: [Mutation Testing: An Empirical Evaluation](https://dl.acm.org/doi/10.1145/3183440) *(requires ACM subscription)*
## License
This example is part of the PMAT project and is provided as-is for demonstration purposes.