From dotnet-test
Analyzes the variety and depth of assertions across .NET test suites. Use when the user asks to evaluate assertion quality, find shallow testing, identify assertion-free tests (no assertions or only trivial ones like Assert.IsNotNull), flag self-referential or tautological assertions (output equals input on identity/round-trip operations), measure assertion coverage diversity, or audit whether tests verify different facets of correctness. Produces metrics and actionable recommendations. Works with MSTest, xUnit, NUnit, TUnit. DO NOT USE FOR: writing new tests (use writing-mstest-tests), other anti-patterns like flakiness or duplication (use test-anti-patterns), or fixing assertions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dotnet-test:assertion-qualityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Analyze .NET test code to measure how varied and meaningful the assertions are. Produce a metrics report that reveals whether tests verify different facets of correctness — not just "output equals X" but also structure, exceptions, state transitions, side effects, and invariants.
Analyze .NET test code to measure how varied and meaningful the assertions are. Produce a metrics report that reveals whether tests verify different facets of correctness — not just "output equals X" but also structure, exceptions, state transitions, side effects, and invariants.
Low assertion diversity signals shallow testing. Tests may pass while bugs hide in unasserted logic. Common symptoms:
| Problem | Symptom | Consequence |
|---|---|---|
| Trivial assertions | Assert.IsNotNull(result) only | Test passes but doesn't verify correctness |
| Single-value obsession | Always check one field or return value | Bugs in unasserted logic slip through |
| No negative assertions | Never check what shouldn't happen | Regressions sneak in through false positives |
| No state checks | Don't verify object state changes | Missed side-effects or lifecycle issues |
| No structural checks | Only assert top-level value | Bugs in nested objects go unnoticed |
| Assertion-free tests | Tests that call but don't verify | Code coverage lies; false security |
writing-mstest-tests)test-anti-patterns)| Input | Required | Description |
|---|---|---|
| Test code | Yes | One or more test files or a test project directory to analyze |
| Production code | No | The code under test, to evaluate whether assertions cover the important behaviors |
Read all test files the user provides. If the user points to a directory or project, scan for all test files — see the dotnet-test-frameworks skill for framework-specific markers.
For each test method, identify all assertions and classify them into these categories:
| Category | Examples | What it verifies |
|---|---|---|
| Equality | Assert.AreEqual, Assert.Equal, Is.EqualTo | Return value matches expected |
| Boolean | Assert.IsTrue, Assert.IsFalse, Assert.True | Condition holds |
| Null checks | Assert.IsNull, Assert.IsNotNull, Assert.NotNull | Presence/absence of value |
| Exception | Assert.ThrowsException, Assert.Throws, Assert.ThrowsAsync | Error handling behavior |
| Type checks | Assert.IsInstanceOfType, Assert.IsAssignableFrom | Runtime type correctness |
| String | StringAssert.Contains, StringAssert.StartsWith, Assert.Matches | Text content and format |
| Collection | CollectionAssert.Contains, Assert.Contains, Assert.All, Has.Member | Collection contents and structure |
| Comparison | Assert.IsTrue(x > y), Assert.InRange, Is.GreaterThan | Ordering and magnitude |
| Approximate | Assert.AreEqual(expected, actual, delta), Is.EqualTo().Within() | Floating-point or tolerance-based |
| Negative | Assert.AreNotEqual, Assert.DoesNotContain, Assert.DoesNotThrow | What should NOT happen |
| State/Side-effect | Assertions on object properties after mutation, verifying mock calls | State transitions and side effects |
| Structural/Deep | Assertions on nested properties, serialized forms, complex objects | Deep object correctness |
A single assertion can belong to multiple categories (e.g., Assert.AreNotEqual is both Equality and Negative).
Calculate these metrics for the test suite:
Assert.IsTrue(true) — trivial means no meaningful value verificationAssert.AreEqual(input, Parse(input.ToString()))) or assert a field against itself (Assert.AreEqual(dto.Name, dto.Name)). These are tautological — they verify the plumbing, not the behavior.Before reporting, calibrate findings:
Assert.IsNotNull(result) alone is trivial. But Assert.IsNotNull(result) followed by Assert.AreEqual(expected, result.Value) is not — the null check is a guard before the real assertion. Only flag a test as "trivial" if it has no meaningful value assertions.Assert.IsTrue(result.IsValid) checks a specific property — it's a Boolean assertion, not a trivial one. Assert.IsTrue(true) is trivial.Assert.IsTrue.Assert.ThrowsException<T>(() => ...) may be the only assertion — that's fine for exception-focused tests. Don't penalize them for low assertion count.Assert.AreEqual calls has high volume but low diversity. A test with one equality, one null check, and one exception assertion has low volume but good diversity.Assert.AreEqual(input, roundTrip(input)) looks like a real equality assertion but is tautological when the operation under test is expected to be identity. Flag these separately from normal equality assertions. If the test's purpose is to verify a round-trip (serialize/deserialize, encode/decode), the assertion is valid — but it should be accompanied by assertions on non-trivial inputs that exercise the transformation.Present the analysis in this structure:
Summary Dashboard — A quick-reference table of key metrics:
| Metric | Value | Assessment |
|-------------------------------|--------|------------|
| Total tests | 25 | — |
| Average assertions per test | 2.4 | Moderate |
| Assertion type spread | 5/12 | Low |
| Tests with zero assertions | 3 (12%)| Concerning |
| Tests with only trivial asserts | 4 (16%)| Acceptable |
| Tests with negative assertions | 2 (8%) | Below target |
| Single-category tests | 15 (60%)| High |
Category Breakdown — For each assertion category, show:
Gap Analysis — Based on the production code (if available), identify:
Recommendations — Prioritized list of improvements:
Assertion-free tests — If any exist, list each one with its method name and what it appears to be testing, so the user can decide whether to add assertions or mark them as intentional smoke tests.
| Pitfall | Solution |
|---|---|
| Penalizing exception tests for low assertion count | Exception assertions are complete on their own — skip count warnings for these |
| Flagging null checks before value checks as trivial | Only flag tests where the null check is the ONLY assertion |
Counting Assert.IsTrue(condition) as trivial | Only Assert.IsTrue(true) or always-true conditions are trivial |
| Ignoring framework differences | MSTest uses Assert.AreEqual, xUnit uses Assert.Equal, NUnit uses Is.EqualTo — classify all correctly |
| Recommending diversity for diversity's sake | Only suggest adding assertion types that would catch real bugs in the code under test |
| Missing implicit assertions | Assert.ThrowsException is both an exception assertion and a negative assertion (verifying that calling the method has a specific failure mode) |
npx claudepluginhub weiflycc-cmd/skills --plugin dotnet-testGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.