Types that used to be 100% covered by tests should still be 100% covered

Rule ND1606 Code Coverage

Runs only in diff mode (baseline defined) and when coverage data is imported. Warns when a type fully covered by tests in the baseline is now only partially covered.

The last ~10% of a class is as hard to cover as the first 90%, but untestable code usually means poorly written, error prone code: most tricky bugs might come from this small portion of hard-to-test code. Most application logic should live in easy-to-test classes 100% covered.

Severity is High: a type that lost 100% coverage is a bug-prone situation to handle carefully.

How to fix

Write more unit-tests for code no longer covered. Hard-to-test code signals a design needing refactoring. 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>Types that used to be 100% covered by tests should still be 100% covered</Name>
// <Id>ND1606:TypesThatUsedToBe100PercentCoveredByTestsShouldStillBe100PercentCovered</Id>

warnif count > 0
from t in JustMyCode.Types where 
   t.IsPresentInBothBuilds() &&
   t.OlderVersion().PercentageCoverage == 100 &&
   t.PercentageCoverage < 100

from m in t .MethodsAndConstructors where 
  m.NbLinesOfCode> 0 && 
  m.PercentageCoverage < 100 &&
 !m.IsExcludedFromCoverage

select new {
   m, 
   m.PercentageCoverage, 

   // Simplistic Debt estimation, because the effort to write tests for a method not 100% tested
   // is already estimated properly with the rule "Code should be tested".
   Debt = t.NbLinesOfCodeNotCovered.Linear(1,2, 10,10).ToMinutes().ToDebt(),

   Severity = Severity.High
}

//<Expl>
// The *{0}* was previously 100% covered by tests in the baseline, but now it stands at {1}% coverage.
// It is important to create new tests to potentially detect any regressions in coverage that might introduce bugs.
//</Expl>

//<Desc>
// Runs only in *diff mode* (baseline defined) and when coverage data is
// imported. Warns when a type fully covered by tests in the baseline is now
// only partially covered.
//
// The last ~10% of a class is as hard to cover as the first 90%, but
// *untestable code* usually means *poorly written*, *error prone* code:
// **most tricky bugs might come from this small portion of hard-to-test
// code**. Most application logic should live in *easy-to-test classes* 100%
// covered.
//
// Severity is **High**: a type that lost 100% coverage is a bug-prone
// situation to handle carefully.
//</Desc>

//<HowToFix>
// Write more unit-tests for code no longer covered. *Hard-to-test code*
// signals a design needing refactoring. For code impossible to cover (like
// *MessageBox.Show()*), define an infrastructure to *mock* it at test-time.
//</HowToFix>

Code Coverage: all 15 items