pytest plugin that calculates CRAP scores to guide test writing
Project description
pytest-crap
A pytest plugin that calculates CRAP scores and displays prioritized lists of high-risk functions to guide test writing.
What is CRAP?
CRAP stands for Change Risk Anti-Patterns (or, more colloquially, "Change Risk Analysis and Predictions"). The metric was introduced by Alberto Savoia and Bob Evans to help developers identify code that is both complex and poorly tested—a risky combination.
The CRAP score combines two factors:
- Cyclomatic Complexity (CC): A measure of how many independent paths exist through your code. More branches (
if,for,while,try, etc.) means higher complexity. - Code Coverage: The percentage of lines executed by your tests.
The formula is:
CRAP(m) = CC(m)² × (1 - cov(m))³ + CC(m)
Where CC(m) is the cyclomatic complexity and cov(m) is the code coverage (0.0 to 1.0) for method m.
Why Use CRAP Scores?
- Prioritize testing efforts: Focus on functions that are both complex AND under-tested
- Identify risky code: High CRAP scores indicate code that's likely to harbor bugs and is difficult to change safely
- Track improvement: Monitor CRAP scores over time to ensure code quality improves
Interpreting CRAP Scores
| Score | Interpretation |
|---|---|
| < 5 | Excellent — low complexity, well tested |
| 5–15 | Acceptable — reasonable balance |
| 15–30 | Warning — consider adding tests or simplifying |
| > 30 | Critical — high risk, prioritize for refactoring/testing |
A function with CC=1 and 100% coverage has a CRAP score of 1 (perfect). A function with CC=10 and 0% coverage has a CRAP score of 110 (very high risk).
Installation
pip install pytest-crap
Or with Poetry:
poetry add pytest-crap
Requirements
- Python 3.10+
- pytest 7.0+
- pytest-cov (for coverage data)
Usage
Run pytest with the --crap flag:
pytest --crap
This will run your tests with coverage enabled and display CRAP score tables at the end.
Example Output
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ CRAP by Function ┃
┡━━━━━━━━┯━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ CRAP │ CC │ Coverage │ Function │ File │
├────────┼─────┼──────────┼──────────────────────┼─────────────────────────┤
│ 42.50 │ 8 │ 12.5% │ complex_parser │ src/parser.py │
│ 31.00 │ 5 │ 0.0% │ validate_input │ src/validator.py │
│ 12.25 │ 3 │ 50.0% │ process_data │ src/processor.py │
│ 1.00 │ 1 │ 100.0% │ simple_helper │ src/utils.py │
└────────┴─────┴──────────┴──────────────────────┴─────────────────────────┘
Options
| Option | Default | Description |
|---|---|---|
--crap |
false |
Enable CRAP score reporting |
--crap-threshold |
30 |
CRAP score threshold for highlighting. Functions at or above this value are flagged as high-risk. |
--crap-top-n |
20 |
Number of functions to display in each table. Set to 0 to show all. |
Examples
Show top 10 functions with a stricter threshold:
pytest --crap --crap-threshold=15 --crap-top-n=10
Show all functions regardless of score:
pytest --crap --crap-top-n=0
Combine with other pytest options:
pytest --crap --cov-branch -v tests/
Output Tables
pytest-crap displays three summary tables:
- CRAP by Function: Individual functions ranked by CRAP score
- CRAP by File: Files ranked by their highest CRAP score, with count of functions above threshold
- CRAP by Folder: Directories ranked by highest CRAP score
Integration with CI
Add to your CI pipeline to track CRAP scores. Example GitHub Actions step:
- name: Run tests with CRAP reporting
run: pytest --crap --crap-threshold=30
Contributing
See contributing.md for development setup and guidelines.
License
MIT
References
- Original CRAP metric paper by Alberto Savoia
- Cyclomatic Complexity on Wikipedia
- radon - Python library for code metrics
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_crap-0.3.0.tar.gz.
File metadata
- Download URL: pytest_crap-0.3.0.tar.gz
- Upload date:
- Size: 20.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8267dbf4e83f8ad1430aac86aa1592f99d658dc1a9882dcfc2ee5775dbb5d0cc
|
|
| MD5 |
0b4a5624f12e94890f02087681ed728a
|
|
| BLAKE2b-256 |
23a7fde44fbcbf621b1be3c58081a4ce52abd06f9fef4608f21284fe9ef4a3ab
|
Provenance
The following attestation bundles were made for pytest_crap-0.3.0.tar.gz:
Publisher:
publish.yml on ChristianMurphy/pytest-crap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_crap-0.3.0.tar.gz -
Subject digest:
8267dbf4e83f8ad1430aac86aa1592f99d658dc1a9882dcfc2ee5775dbb5d0cc - Sigstore transparency entry: 737243663
- Sigstore integration time:
-
Permalink:
ChristianMurphy/pytest-crap@e059b3ff341bd583ccdf8789e3279826dc29fdc6 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/ChristianMurphy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e059b3ff341bd583ccdf8789e3279826dc29fdc6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pytest_crap-0.3.0-py3-none-any.whl.
File metadata
- Download URL: pytest_crap-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baf7c3e60f8b8f0edbb50190a32f7e43ae4270e6fa48eb15ffd0a3c72ea45445
|
|
| MD5 |
4786731126978345c7032a7bbc26b9f7
|
|
| BLAKE2b-256 |
69eaf05a7cb2a0109453916ffea829e1ed390779edeff01ed96a8eac0a3af257
|
Provenance
The following attestation bundles were made for pytest_crap-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on ChristianMurphy/pytest-crap
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_crap-0.3.0-py3-none-any.whl -
Subject digest:
baf7c3e60f8b8f0edbb50190a32f7e43ae4270e6fa48eb15ffd0a3c72ea45445 - Sigstore transparency entry: 737243674
- Sigstore integration time:
-
Permalink:
ChristianMurphy/pytest-crap@e059b3ff341bd583ccdf8789e3279826dc29fdc6 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/ChristianMurphy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e059b3ff341bd583ccdf8789e3279826dc29fdc6 -
Trigger Event:
release
-
Statement type: