Methods should have a low C.R.A.P score

Rule ND1609 Code Coverage

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.

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>

Code Coverage: all 15 items