Best for
- A PR automation workflow needs to post a comment grading the tests
- A reviewer has a specific list of tests (a file, a class, a method list,
- A maintainer wants to triage which of N tests in a contribution deserve
dotnet/skills/plugins/dotnet-test/skills/grade-tests/SKILL.md
Grades a specified set of test methods individually and produces a concise table mapping each test (fully-qualified name) to a letter grade (A–F), a score band, and a one-line note — designed to be posted as a PR comment. Use when the caller wants per-test feedback on a curated list of methods (for example, the new or modified tests in a pull request), not a suite-wide audit. Polyglot: .NET, Python, TS/JS, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. Input is a list of test methods (or
Decision brief
Grade a curated list of test methods and produce a compact, PR-comment-friendly report: one row per test method with a letter grade, a score band, and a one-line note explaining the grade. The skill does not discover tests on its own — the caller (typically a PR automation workf…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/dotnet/skills --skill "plugins/dotnet-test/skills/grade-tests"Inspect the Agent Skill "grade-tests" from https://github.com/dotnet/skills/blob/73555e9231867c5978db07191514b7beb22cd253/plugins/dotnet-test/skills/grade-tests/SKILL.md at commit 73555e9231867c5978db07191514b7beb22cd253. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Before doing anything else, check that the caller provided one of:
Identify the target codebase's language and test framework from the file extensions and the test method markers in the provided list. Call the test-analysis-extensions skill and read the matching extension file (e.g., extensions/dotnet.md for MSTest/xUnit/NUnit/TUnit, extensions…
Identify the target codebase's language and test framework from the file extensions and the test method markers in the provided list. Call the test-analysis-extensions skill and read the matching extension file (e.g., extensions/dotnet.md for MSTest/xUnit/NUnit/TUnit, extensions…
For each entry in the input list:
Start every test at grade A (score band 90–100), then apply deductions strictly for observable issues in the captured body. Do not deduct for hypothetical concerns (e.g., "could have more negative assertions") unless the production code clearly demands them and the production co…
Permission review
The documentation asks the agent to read local files, directories, or repositories.
to discover available extension files, then read the file matching theThe documentation asks the agent to read local files, directories, or repositories.
You MUST read the relevant extension file before scoring assertions orEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 95/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 5,241 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Grade a curated list of test methods and produce a compact, PR-comment-friendly report: one row per test method with a letter grade, a score band, and a one-line note explaining the grade. The skill does not discover tests on its own — the caller (typically a PR automation workflow or a human reviewer holding a specific list) provides the test methods to grade.
Language-specific guidance: Call the
test-analysis-extensionsskill to discover available extension files, then read the file matching the target codebase's language and framework (e.g.,extensions/dotnet.md,extensions/python.md,extensions/typescript.md,extensions/go.md). You MUST read the relevant extension file before scoring assertions or anti-patterns, because assertion APIs and idiomatic patterns differ significantly across frameworks.
Suite-wide audits (test-anti-patterns, assertion-quality,
test-smell-detection) produce excellent diagnostic reports, but they are
hard to consume as a short PR comment. Reviewers of a PR mostly want to know:
for the tests this PR adds or changes, are they good? This skill answers
that question with a one-row-per-test verdict that fits in a comment table.
test-anti-patterns (pragmatic) or test-smell-detection (formal) and
let the test-quality-auditor agent orchestrate.code-testing-generator
(any language) or writing-mstest-tests (MSTest specifically).coverage-analysis or crap-score (.NET only).| Input | Required | Description |
|---|---|---|
| Test methods | Yes | A scope to grade. Provide one of: (a) an explicit list of test method names (fully-qualified, e.g. Namespace.ClassName.TestMethodName); (b) one or more file paths plus an explicit instruction to grade every test declared in those files; or (c) a diff hunk / PR identifier whose changed tests should be graded. File paths are recommended but optional when method names are unambiguous in the workspace. Ambiguous requests like "grade my tests" with no scope are rejected up-front (see Step 0); this skill is for curated input and does not auto-grade an entire workspace. |
| Test bodies / spans | Recommended | The exact source lines for each test method. If omitted, read them from the listed files. |
| Production code | No | The code under test, for judging whether assertions cover the meaningful behaviors. When unavailable, mark relevant findings as "Unverified" rather than guessing. |
| Diff context | No | When grading PR changes, the unified diff for each test method helps focus on what actually changed. |
Before doing anything else, check that the caller provided one of:
OrderTests.cs"), orIf the request is ambiguous (e.g., "Grade my tests", "Are these tests
any good?" with no scope, "Review the test suite"), do not load
extensions, do not read files, and do not grade anything. Reply with a
short message asking the caller to provide an explicit list / file(s) /
diff, and optionally point them at test-quality-auditor agent or
test-anti-patterns skill for full-suite analysis. Stop there.
Identify the target codebase's language and test framework from the file
extensions and the test method markers in the provided list. Call the
test-analysis-extensions skill and read the matching extension file (e.g.,
extensions/dotnet.md for MSTest/xUnit/NUnit/TUnit, extensions/python.md
for pytest, extensions/typescript.md for Jest/Vitest, extensions/go.md
for the standard testing package). If the input contains tests from
multiple languages, load each relevant extension and grade each test using
its language's conventions.
For each entry in the input list:
N/A — method not found and
continue. Never invent a body to grade.Start every test at grade A (score band 90–100), then apply deductions strictly for observable issues in the captured body. Do not deduct for hypothetical concerns (e.g., "could have more negative assertions") unless the production code clearly demands them and the production code is available.
When production code is unavailable, grade observable issues in the test body
normally, but do not infer missing behaviors or deduct for them. State
Production-dependent behavior coverage: Unverified once in the summary so the
reader can distinguish test-body findings from claims that require source code.
Compute three sub-grades (each A–F) that together drive the overall grade.
Read the loaded language extension's assertion API list and classify every assertion in the test body. Score from highest to lowest:
| Sub-grade | Pattern |
|---|---|
| A | At least one meaningful value assertion (equality / structural / exception / state) plus, where appropriate, additional checks (negative, type, collection contents). Mock-call verifications (Verify, toHaveBeenCalledWith, Should -Invoke) and bare assertion forms (pytest assert, Go if got != want { t.Errorf(...) }, Rust assert!()) count as real assertions. |
| B | One clear meaningful assertion that verifies the behavior under test. |
| C | Only trivial assertions (single IsNotNull / toBeDefined / assert x is not None), or assertions that check a single field while the operation produces a richer result. |
| D | One self-referential / tautological assertion (Assert.AreEqual(x, x), assert dto.name == dto.name, round-trip identity without a non-trivial input), or broad exception assertions (Assert.ThrowsException<Exception>). |
| F | No assertions at all; all assertions are always-true literals (Assert.IsTrue(true), assert True, expect(true).toBe(true)) — these verify nothing and are equivalent to having no assertions; or all assertions are silently un-awaited (e.g., expect(promise).resolves.toBe(x) without await/return, async TUnit/xUnit Assert.ThrowsAsync without await, pytest-asyncio with un-awaited coroutine). |
Exception and error-path tests (Assert.ThrowsException<T>, constrained
pytest.raises, expect(fn).toThrow, assertThrows, #[should_panic],
Should -Throw, EXPECT_THROW, or Go code that verifies an expected non-nil
error) are complete on their own. Give Assertion strength A when the test
checks the exact promised error condition for its stated scope. Do not deduct
for having only that assertion, and do not require an error-message assertion
unless the message is part of the documented contract. A Go happy-path test
that only checks err == nil while discarding a meaningful returned value is
still C because it does not verify the successful result.
| Sub-grade | Pattern |
|---|---|
| A | Clear Arrange-Act-Assert (or Given-When-Then) separation. Single behavior under test. Body under ~30 lines. Setup uses framework conventions. |
| B | One mild structural issue (slightly long body, missing blank lines between phases) but intent is clear. |
| C | Multiple behaviors mixed in one test, or AAA phases interleaved enough to slow comprehension. |
| D | Conditional logic in the test (if/switch driving assertions) — except for idiomatic Go/Rust table-driven sub-test loops; or test relies on previous test state (ordering dependency). |
| F | Test exceeds ~60 lines and verifies multiple unrelated behaviors; or shares mutable state with other tests through statics/globals without reset. |
Scan against the catalog below. The Anti-pattern sub-grade is computed in two passes and combined deterministically:
The final Anti-pattern sub-grade is the worse of the two passes
(i.e., min(hard_ceiling, A − medium_count)). Low findings never
affect the grade — mention them in the note only.
Examples (Critical/High and Medium counts → Anti-pattern sub-grade):
min(C, A − 2 = C) = C, but a third Medium would tip to D)Critical (drop straight to F or D)
try { … } catch { } (.NET), bare except: pass
(Python), try { … } catch (e) {} (JS/TS/Java), defer recover()
without re-panic (Go), rescue StandardError with no assertion (Ruby),
empty catch (Kotlin/Swift) → FAssert.Fail(ex.Message) instead of
Assert.ThrowsException) → DAssert.IsTrue(true), assert True,
expect(true).toBe(true)) → F (verifies nothing; also drives
Assertion sub-grade to F)Assert.AreEqual(x, x), assert dto.name == dto.name) → DHigh (drop one or two sub-grades)
Thread.Sleep, Task.Delay,
time.sleep, setTimeout-based wait, Thread.sleep, time.Sleep,
sleep, std::thread::sleep, Start-Sleep,
std::this_thread::sleep_for (in a unit test) → DDateTime.Now, datetime.now(), Date.now(),
System.currentTimeMillis(), time.Now(), Time.now,
Instant::now(), Get-Date, system_clock::now) → DC:\…, /tmp/…, network hosts) → DAssert.ThrowsException<Exception>,
pytest.raises(Exception), expect(fn).toThrow(Error) without matcher,
#[should_panic] without expected = "…", Should -Throw without
-ExpectedMessage, EXPECT_ANY_THROW) → CMedium (drop one sub-grade)
Test1, TestMethod, test, single-word name that says
nothing about scenario or expected outcome (judge against the language
extension's convention) → drop one sub-grade42, "foo", 0x1234 in arrange/assert
without naming or comment → drop one sub-gradeLow (note only, no deduction)
Console.WriteLine,
print, console.log, System.out.println, fmt.Println, puts,
dbg!, Write-Host, std::cout); inconsistent naming versus siblings;
leftover TODO comments. Mention in the note column but do not deduct.Convert sub-grades to numeric points: A=4, B=3, C=2, D=1, F=0.
0.45 × Assertion + 0.30 × Anti-pattern + 0.25 × StructureReport the letter grade and the score band (not a single 0–100 number). False precision invites bikeshedding; bands keep the conversation focused on the rubric.
The note column is one short sentence (target ≤ 120 characters). State the single most important reason for the grade. Examples:
Clear AAA structure; equality + exception assertions on the public contract.Good assertion variety, mildly long body — consider splitting into per-condition tests.Only checks IsNotNull on the result; no value verification.Self-referential assertion: round-trip identity verifies plumbing, not transformation.No assertions — test executes the method but never verifies anything.If a test gets A with no notable issues, the note may simply be
No issues found. — do not invent weaknesses to justify the grade.
Produce two sections.
A short paragraph (2–4 sentences) covering: total tests graded, grade distribution, most common issue, and the single most important recommendation.
| Test | Grade | Band | Notes |
|------|-------|------|-------|
| `Namespace.ClassName.Test_Method_Condition_Expected` | A | 90–100 | Clear AAA; equality + exception assertions. |
| `Namespace.ClassName.Test_Other` | C | 70–79 | Only `IsNotNull` — no value verification. |
| `Namespace.ClassName.Test_Old` | F | 0–59 | No assertions. |
Caps and ordering:
<details> block.(new) or
(modified) marker.If multiple languages are present, produce one table per language and prefix each section with the language name and framework.
N/A — method not found).Assert.IsTrue(result.IsValid))
are not classified as always-true; only literal true/false constants are.assert, Go if got != want { t.Errorf(...) },
JS/TS expect(mock).toHaveBeenCalledWith(...).resolves/rejects/ThrowsAsync,
pytest-asyncio without await) drop the Assertion sub-grade to F.| Pitfall | Solution |
|---|---|
| Grading every test in the workspace when no list is provided | Ask the caller for the explicit list; this skill is for curated input. |
| Inflating deductions to justify the grade | Start at A; deduct only for observable issues. |
| Penalizing exception tests for low assertion count | Exception assertions are complete on their own. |
Downgrading a focused Go error-path test because it checks only err != nil | Expected-error existence is the observable contract for that scope; keep it at A unless the production contract requires a specific error identity or message. |
Treating IsNotNull before a value assertion as trivial | Only flag when the null check is the only assertion. |
| Treating any Boolean assertion as effectively assertion-free | Only always-true literals (Assert.IsTrue(true), assert True) are; meaningful Assert.IsTrue(result.IsValid) is a real assertion. |
| Flagging Go/Rust table-driven loops as conditional logic | They are idiomatic; do not deduct. |
Treating pytest bare assert or Go if got != want { t.Error… } as missing-framework | Both are canonical; count in the correct assertion category. |
| Penalizing tests when production code is unavailable | Mark concerns about uncovered behaviors as Unverified and do not deduct. |
| Using a fake-precise score (e.g., 87/100) | Use the score band only — 90–100, 80–89, 70–79, 60–69, 0–59. |
| Spilling a 500-row table into a PR comment | Apply the row cap from Step 5; collapse extras into <details>. |
| Re-reporting an existing finding three times under different categories | Pick the most fitting category and report once. |
| Inventing weaknesses for A-grade tests to make the note "balanced" | If a test is clean, the note may simply read No issues found. |
Frequently asked questions
Grade a curated list of test methods and produce a compact, PR-comment-friendly report: one row per test method with a letter grade, a score band, and a one-line note explaining the grade. The skill does not discover tests on its own — the caller (typically a PR automation workf…
The source record exposes this install command: npx skills add https://github.com/dotnet/skills --skill "plugins/dotnet-test/skills/grade-tests". Inspect the command and pinned source before running it.
Static rules flagged read-files in the source; the page lists the matching lines and excerpts.
Alternatives
VincentChuWaiChow/vanguard-frontier-agentic
Retrieves and analyzes Apex debug logs from a connected Salesforce org to identify governor-limit hits, SOQL N+1 patterns, unhandled exceptions, and async job failures. T1 read-only runtime — retrieves logs only, never executes code or mutates data. TRIGGER when: user asks to analyze an Apex log, debug a trigger failure, diagnose a governor limit hit, interpret a stack trace from a Salesforce org, or review a DEBUG log for performance issues. Trigger phrases: analyze apex log, debug this trigger
johnqtcg/awesome-skills
Review Go code with a defect-first approach using repository policy (constitution.md first, then AGENTS.md fallback). Use for code review, PR review, quality checks, risk analysis, and regression detection.
NVIDIA/skills
Autonomous NeMo-RL research agent workflow for directed hypothesis testing and open-ended discovery. Guides agents through the full experiment lifecycle: understanding recipes and environments, wiring RL or NeMo-gym runs, launching reproducible baselines and iterations, analyzing results, preserving human oversight, and using git plus TSV logs as the research ledger. Do NOT use for: bug fixes, code review, documentation, refactoring, dependency updates, or single-file changes.
adriannoes/awesome-agentic-ai
This skill covers implementing Software Composition Analysis (SCA) using Snyk to detect vulnerable open-source dependencies in CI/CD pipelines. It addresses scanning package manifests and lockfiles, automated fix pull request generation, license compliance checking, continuous monitoring of deployed applications, and integration with GitHub, GitLab, and Jenkins pipelines.