Methods should have a low C.R.A.P score
Runs only when coverage data is imported.
Change Risk Analyzer and Predictor (CRAP) pinpoints code both complex and untested. First defined here: http://www.artima.com/weblogs/viewpost.jsp?thread=215899
Formula: CRAP(m) = CC(m)^2 * (1 – cov(m)/100)^3 + CC(m), where CC(m) is the cyclomatic complexity and cov(m) the percentage coverage.
Matched methods cumulate two error prone smells: complexity (hard to maintain) and incomplete coverage (hard to refactor without regression). The threshold of 30 is the one suggested by the inventors; no amount of testing keeps methods with CC > 30 out of CRAP territory. Methods with fewer than 10 lines of code are not matched.
Change Risk Analyzer and Predictor (CRAP) pinpoints code both complex and untested. First defined here: http://www.artima.com/weblogs/viewpost.jsp?thread=215899
Formula: CRAP(m) = CC(m)^2 * (1 – cov(m)/100)^3 + CC(m), where CC(m) is the cyclomatic complexity and cov(m) the percentage coverage.
Matched methods cumulate two error prone smells: complexity (hard to maintain) and incomplete coverage (hard to refactor without regression). The threshold of 30 is the one suggested by the inventors; no amount of testing keeps methods with CC > 30 out of CRAP territory. Methods with fewer than 10 lines of code are not matched.
How to fix
Refactor the complex logic into smaller, less complex methods (possibly in new types) and write unit-tests to fully cover it. For code impossible to cover (like MessageBox.Show()), define an infrastructure to mock it at test-time.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Methods should have a low C.R.A.P score</Name>
// <Id>ND1609:MethodsShouldHaveALowCRAPScore</Id>
warnif count > 0
from m in JustMyCode.Methods
// Don't match too short methods
where m.NbLinesOfCode > 10 && m.CoverageDataAvailable && m.CyclomaticComplexity != null
let CC = m.CyclomaticComplexity
let uncov = (100 - m.PercentageCoverage) / 100f
let CRAP = (double)(CC * CC * uncov * uncov * uncov) + CC
where CRAP != null && CRAP > 60
orderby CRAP descending, m.NbLinesOfCode descending
select new {
m,
CRAP,
CC,
m.PercentageCoverage, m.NbLinesOfCode,
// CRAP score equals 30 => 10 minutes debt
// CRAP score equals 3000 => 3 hours to write tests
Debt = CRAP.Linear(30,10, 3000, 3*60).ToMinutes().ToDebt(),
Severity = Severity.High
}
//<Expl>
// The *{0}* has a an insufficient CRAP score of *{1}*.
// The *Change Risk Analyzer and Predictor* code metric
// is specifically crafted to identify code plagued by both
// excessive complexity and a low testing coverage ratio.
//</Expl>
//<Desc>
// Runs only when coverage data is imported.
//
// **Change Risk Analyzer and Predictor** (CRAP) pinpoints code both complex
// and untested. First defined here:
// http://www.artima.com/weblogs/viewpost.jsp?thread=215899
//
// Formula: **CRAP(m) = CC(m)^2 * (1 – cov(m)/100)^3 + CC(m)**, where *CC(m)*
// is the *cyclomatic complexity* and *cov(m)* the *percentage coverage*.
//
// Matched methods cumulate two *error prone* smells: complexity (hard to
// maintain) and incomplete coverage (hard to refactor without regression).
// The threshold of 30 is the one suggested by the inventors; no amount of
// testing keeps methods with CC > 30 out of CRAP territory. Methods with
// fewer than 10 lines of code are not matched.
//</Desc>
//<HowToFix>
// Refactor the complex logic into smaller, less complex methods (possibly in
// new types) and write unit-tests to fully cover it. For code impossible to
// cover (like *MessageBox.Show()*), define an infrastructure to *mock* it at
// test-time.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Code Coverage: all 15 items
- Code should be tested ND1600
- New Types and Methods should be tested ND1601
- Methods refactored should be tested ND1602
- Assemblies and Namespaces should be tested ND1603
- Types almost 100% tested should be 100% tested ND1604
- Namespaces almost 100% tested should be 100% tested ND1605
- Types that used to be 100% covered by tests should still be 100% covered ND1606
- Types tagged with FullCoveredAttribute should be 100% covered ND1607
- Types 100% covered should be tagged with FullCoveredAttribute ND1608
- Methods should have a low C.R.A.P score ND1609
- Complex Methods should be 100% tested ND1610
- Test methods should assert something ND1611
- Test Methods
- Methods directly called by test Methods
- Methods directly and indirectly called by test Methods