From now, all types added should be 100% covered by tests
Runs only in diff mode (baseline defined) and when coverage data is imported, on types added since baseline (easily modified to also match refactored types) not fully covered by tests.
The last ~10% of a class is as hard to cover as the first 90%, so teams often settle for 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. Not all classes need 100% coverage (e.g. UI), but most of the application logic should live in easy-to-test classes that are 100% covered.
The last ~10% of a class is as hard to cover as the first 90%, so teams often settle for 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. Not all classes need 100% coverage (e.g. UI), but most of the application logic should live in easy-to-test classes that are 100% covered.
How to fix
Write more unit-tests for uncovered code. 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.
Constant 10 minutes Debt: the testing effort is already estimated by the Code Coverage rules. But severity is High, higher for new types: the time to write tests is now, before the next production release.
Constant 10 minutes Debt: the testing effort is already estimated by the Code Coverage rules. But severity is High, higher for new types: the time to write tests is now, before the next production release.
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>From now, all types added should be 100% covered by tests</Name>
// <Id>ND1101:FromNowAllTypesAddedShouldBe100PercentCoveredByTests</Id>
warnif count > 0
// Gather typesMoved, types whose parent namespaces changed since baseline
let typesRemoved = context.CompareContext.OlderCodeBase.Application.Types
.Where(t => t.WasRemoved()).Select(t => t.Name).ToHashSetEx()
let typesMoved = Application.Types
.Where(t => t.WasAdded() && typesRemoved.Contains(t.Name)).ToHashSetEx()
from t in JustMyCode.Types where
// Only match types added since Baseline.
// Uncomment this line to match also refactored types since Baseline.
// (t.WasAdded() || t.CodeWasChanged()) &&
t.WasAdded() &&
// …that are not 100% covered by tests
t.PercentageCoverage < 100 &&
// Don't match types moved from one namespace to another
!typesMoved.Contains(t)
let methodsCulprit = t.Methods.Where(m => m.PercentageCoverage < 100)
select new {
t,
t.PercentageCoverage,
methodsCulprit,
t.NbLinesOfCodeNotCovered,
t.NbLinesOfCode,
// Constant Debt estimation, since for such type rules in category "Coverage"
// accurately estimate the untested code Debt.
Debt = 10.ToMinutes().ToDebt(),
// The Severity is higher for new types than for refactored types
AnnualInterest= (t.WasAdded() ? 3 : 1) *
Severity.High.AnnualInterestThreshold()
}
//<Expl>
// The new *{0}* is {1}% covered by tests
// with {3} statement{3?:s} not covered.
// When adding a new type, it is important to also write
// associated unit tests to thoroughly validate its
// functionality before it goes to production.
//</Expl>
//<Desc>
// Runs only in *diff mode* (baseline defined) and when coverage data is
// imported, on types added since baseline (easily modified to also match
// refactored types) not fully covered by tests.
//
// The last ~10% of a class is as hard to cover as the first 90%, so teams
// often settle for 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**. Not all classes need 100% coverage (e.g. UI), but
// most of the application logic should live in *easy-to-test classes* that
// are 100% covered.
//</Desc>
//<HowToFix>
// Write more unit-tests for uncovered code. *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.
//
// Constant 10 minutes Debt: the testing effort is already estimated by the
// **Code Coverage** rules. But severity is **High**, higher for new types:
// the time to write tests is **now**, before the next production release.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Code Smells Regression: all 9 items
- From now, all types added should respect basic quality principles ND1100
- From now, all types added should be 100% covered by tests ND1101
- From now, all methods added should respect basic quality principles ND1102
- Avoid decreasing code coverage by tests of types ND1103
- Avoid making complex methods even more complex ND1104
- Avoid making large methods even larger ND1105
- Avoid adding methods to a type that already had many methods ND1106
- Avoid adding instance fields to a type that already had many instance fields ND1107
- Avoid transforming an immutable type into a mutable one ND1108