# Dotnet Unit Test Coverage

> 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.

- Skill: `newrelic/dotnet-unit-test-coverage` (Agent Skill)
- Install (CLI): `npx skillmds@latest add newrelic/dotnet-unit-test-coverage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/newrelic/dotnet-unit-test-coverage/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: newrelic (https://skillmd.com/u/newrelic)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/newrelic/dotnet-unit-test-coverage

---


# .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.

