| name | dotnet-unit-test-coverage |
| description | Mandatory unit-test and code-coverage rules for the .NET agent. Use when writing an implementation plan, writing or modifying unit tests, or adding/changing production code in the Core project or the NewRelic.Agent.Extensions project. Apply without being prompted by the user. |
.NET Agent Unit-Test and Coverage Requirements
These rules are mandatory and apply automatically -- the user does not need to
ask for tests or for coverage. Honor them when planning work, when writing or
editing tests, and when adding or changing production code.
Scope
- In scope (must be unit-tested): all production code in the
Core project
(src/Agent/NewRelic/Agent/Core/) and the NewRelic.Agent.Extensions project
(src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/).
- Out of scope: the wrapper projects under
src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/*. They have no unit
tests by design (covered by the Integration / Unbounded / Container test
solutions). When adding non-trivial logic to a wrapper, lift it into a helper
in NewRelic.Agent.Extensions and unit-test the helper -- see the wrapper
guidance in tests/CLAUDE.md and the top-level CLAUDE.md.
Rule 1: New testable code gets unit tests in the same change
Adding or changing in-scope production code without accompanying unit tests is
incomplete work, not a follow-up task. Write the tests as part of the same
change, unprompted. Use TDD where practical: write the failing test first, see
it fail, implement, see it pass.
Rule 2: Aim for 100% code coverage
When writing or modifying unit tests, cover every reachable line and branch of
the code under test:
- Include error /
catch paths, early returns, and boundary conditions
(just-under / just-over a cap, empty / null inputs, first-and-last iterations).
- The only acceptable uncovered code is a genuinely unreachable defensive guard
(for example an
IsNaN check on a value that the parser can never produce a
NaN for, or a default: no input can hit). Call these out explicitly rather
than contorting tests to fake-cover dead code.
- Do not pad coverage with assertion-free tests. Every test must verify
behavior, not merely execute lines.
When writing implementation plans
Bake these rules into the plan so the implementer applies them without
re-deriving them:
- In the plan's Global Constraints header, state: "All new/changed code in
Core and NewRelic.Agent.Extensions must ship with unit tests in the same
task; target 100% reachable line and branch coverage."
- Every task that creates or modifies in-scope production code must include
explicit TDD steps (write failing test -> run/fail -> implement -> run/pass ->
commit) with the actual test code, not a "write tests for the above"
placeholder.
- A task that only touches wrapper code (and cannot have its logic lifted into a
helper) is the sole exception -- note that it is covered by integration tests
instead.
Mechanics
Test layout, frameworks (NUnit + JustMock Lite, interfaces/virtual only), the
SolutionDir build caveat, and the "run against the built DLL for
NewRelic.Agent.Extensions.Tests" caveat are in the top-level CLAUDE.md
("Building and testing from the CLI", "Testing conventions") and
tests/CLAUDE.md.
Achieving coverage without piercing encapsulation
JustMock Lite mocks only interfaces and virtual members -- no sealed, static,
or non-virtual mocking -- and InternalsVisibleTo is banned in every
production and test assembly. So when in-scope code is hard to reach from a
test, do NOT widen visibility or expose internals to the test assembly. Make
the code coverable by refactoring the production type to expose a proper seam:
extract an interface, make the member virtual, inject a mockable dependency,
or lift the logic into a public helper. If a line is only reachable by mocking
a private/sealed/static member or via InternalsVisibleTo, the design -- not
the test -- is what must change.