Methods refactored should be tested

Rule ND1602 Code Coverage

Runs only in diff mode (baseline defined) and when coverage data is imported, on methods refactored since baseline.

Covered code is maintainable and non-error-prone. Since stopping development to test a poorly covered code base is impracticable, each time a method or type gets refactored, write associated unit-tests. Maintainability increases, refactoring becomes driven by testability, and the overall design improves.

Severity is High: these issues reflect an actual trend of not writing tests for refactored code.

How to fix

Write unit-tests covering most refactored methods and classes.

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 refactored should be tested</Name>
// <Id>ND1602:MethodsRefactoredShouldBeTested</Id>

warnif count > 0
from m in JustMyCode.Methods where
  m.PercentageCoverage < 30 &&
  m.CodeWasChanged() &&

 !m.IsClassConstructor &&
  // Make sure the source file really changed, and it is not just a IL change
  m.SourceFileDeclAvailable &&
  context.CompareContext.WasChanged(m.SourceDecls.First().SourceFile.FilePath)

// A change that keeps the method's shape — same lines of code, same IL size, same
// cyclomatic complexity — is the ripple of a rename, a namespace move or a retyped
// dependency (a type reference or a call/callvirt swap in the IL), not a refactoring
// of this method: nothing new to test here.
let older = m.OlderVersion()
where older == null ||
      m.NbLinesOfCode != older.NbLinesOfCode ||
      m.NbILInstructions != older.NbILInstructions ||
      m.CyclomaticComplexity != older.CyclomaticComplexity

  orderby m.NbLinesOfCode descending,
           m.NbLinesOfCodeNotCovered ,
           m.PercentageCoverage
select new {
   m, 
   m.PercentageCoverage,
   m.NbLinesOfCode, 
   m.NbLinesOfCodeNotCovered,

   // 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 = m.NbLinesOfCodeNotCovered.Linear(1,2, 10,10).ToMinutes().ToDebt(),

   Severity = Severity.High
}  

//<Expl>The refactored *{0}* is {1}% covered by tests.
// When engaging in code refactoring, it is important to write tests to safeguard against the introduction of regression bugs.</Expl>

//<Desc>
// Runs only in *diff mode* (baseline defined) and when coverage data is
// imported, on methods refactored since baseline.
//
// Covered code is *maintainable* and *non-error-prone*. Since stopping
// development to test a poorly covered code base is impracticable, each time
// a method or type gets refactored, write associated unit-tests.
// Maintainability increases, refactoring becomes driven by testability, and
// the overall design improves.
//
// Severity is **High**: these issues reflect an actual trend of not writing
// tests for refactored code.
//</Desc>

//<HowToFix>
// Write unit-tests covering most refactored methods and classes.
//</HowToFix>

Code Coverage: all 15 items