Generated banner for Code Testing
Generated logo for Code Testing

Code Testing

.NET (Microsoft)OfficialPublished by .NET (Microsoft)

  • Skill
  • Workflow
  • Software development
  • MIT
  • No issues found

From the author

ALWAYS USE for test work that requires changes: write, add, generate, repair, or strengthen tests for existing code in xUnit, MSTest, NUnit, pytest, Vitest/Jest, Go, or another framework. Includes regression cases, failing or flaky tests, coverage-driven additions, and audit-then-fix requests. Focused work stays direct; broad or multi-stage work invokes test-engineer. DO NOT USE for only running tests, analysis-only audits, framework/platform migrations, a test blocked on a missing production seam (testability-obstacle), or MSTest API/configuration corrections that do not design new cases (writing-mstest-tests). Within an active test-engineer pipeline, reuse supplied guidance and do not re-enter this skill.
— the description by .NET (Microsoft)

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a standardized pipeline for managing unit test creation and maintenance. It distinguishes between focused tasks, such as adding tests for a single function, and broad tasks, such as comprehensive suite generation. The skill coordinates a Research, Plan, and Implement pipeline to ensure that tests are behavior-focused, compile correctly, and satisfy explicit requirements. It emphasizes the use of concrete assertions and evidence-based reporting, requiring users to map requirements to specific test names.

What it does

  • Generates unit tests for specific files or entire projects.
  • Repairs failing or flaky tests by analyzing production code and test assertions.
  • Strengthens existing test suites through coverage-driven additions.
  • Coordinates a multi-agent pipeline for research, planning, and implementation.
  • Validates test completion against an explicit requirement matrix.
  • Ensures generated tests adhere to project-specific conventions and frameworks.

When to use it

  • When you need to write or add tests for existing code.
  • When you need to repair failing or flaky tests.
  • When you need to improve test coverage for a specific module or project.
  • When you need to ensure new features are covered by unit tests.
  • When you need to generate tests that follow established project conventions.

Advantages

  • Provides a clear, repeatable process for test generation.
  • Ensures tests are directly linked to specific behavioral requirements.
  • Reduces manual effort by automating the research and planning phases.
  • Maintains consistency across test suites through standardized naming and validation.
  • Prevents common pitfalls like weak assertions or ignored tests.

Usage examples

“Generate unit tests for the UserAuthentication class using pytest.”
The agent will analyze the class, create a test plan, implement the tests, and provide a report mapping requirements to the generated test names.
“Repair the failing tests in the order-processing module.”
The agent will reproduce the failure, determine if the issue lies in the test or production code, apply the smallest necessary fix, and verify the fix with a test run.
“Add regression tests for the recently identified bug in the payment gateway.”
The agent will generate a new test case that specifically targets the identified bug, ensuring it passes and prevents future regressions.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/dotnet/code-testing/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip code-testing.zip -d .agents/skills

Documentation by .NET (Microsoft)

Code Testing Skill

The reliable implicit entry point for generating, repairing, and strengthening tests. It handles focused work directly and invokes the public test-engineer agent for broad or multi-stage requests.

Non-negotiable execution contract

Check pipeline ownership first. If the active agent is test-engineer (including a plugin-qualified name such as dotnet-test:test-engineer), or the caller assigned you a phase of that pipeline, do not delegate to another generator. Continue the assigned work inline. This guard takes precedence over every broad-scope delegation instruction below, even if this skill was loaded automatically.

Classify scope before editing:

  • Broad (a project/package-wide suite, or multiple production files/modules): create research.md and plan.md in a resolved non-stageable <TESTAGENT_DIR> before implementation, then status.md there after the final test-quality review. When test-engineer is available, invoke that named custom agent before implementing; do not replace it with a generic subagent carrying the same label or implement the broad request inline. If the state files are absent, the broad workflow is incomplete.

  • Focused (the user explicitly limits work to one function/class/file or one missing method): do not create intermediate state files or fan out to multiple agents. A sparse project-wide request remains broad even when only one source module is present.

For either scope, run the narrowest relevant test command to a clean exit. Always apply Report-safe test names and result validation, including when the caller supplies conventions. Pass this contract to delegated implementers/testers; preserve edge-case data and validate configured reports, not just console output. Keep the handoff proportional: for one to three focused requirements, use a compact bullet list under a Requirement coverage label that names the tests and successful command; for broader or multi-requirement work, use a Requirement | Evidence table. Each requested behavior must cite an exact test name.

Before sending a broad-scope final response, check that the response itself contains | Requirement | Evidence | and exact test names for every behavioral row. A table in a child report or internal plan is not enough. Do not summarize away those names into module-level bullets or an Area | Tests table.

Intermediate state files are internal working data, never deliverables. Keep <TESTAGENT_DIR> non-stageable, never place it or its files in version-controlled workspace content, and never modify .gitignore to hide them.

Treat completeness as a requirement matrix, not a test-count target. Give every independently requested state, boundary, error path, or interaction its own concrete assertion. Combine cases only when one execution genuinely proves the whole requested combination; do not let a parameterized happy-path case stand in for an empty state, invalid discriminator, or before/at/after boundary. For broad requests that name several production modules or layers, give each named module direct tests for its non-trivial public behavior. Cross-module tests prove composition, but do not substitute for the requested module-level coverage. Judge breadth by the behavior matrix, never by matching or exceeding a raw test count.

At the public entry point, delegate broad work to test-engineer once. Research, plan, implementation, and review remain required, but they need not be separate sub-agent calls.

Use only capabilities available in the current runtime. Do not retry a missing skill under aliases or use another agent to retry a policy-denied operation. Read language guidance directly from the caller-provided or runtime-listed code-testing-extensions catalog and its matching language file. It is reference-only, not an invocable skill. If the bundle is absent, use manifests and representative tests and report the missing reference rather than searching installation dirs. If scratch storage is denied, keep the research and plan in context, continue permitted test edits, and report the missing state artifacts. If execution is denied, continue permitted static review and report tests as unrun, never passed. Neither blocker authorizes modifying production code or weakening requirements.

For a broad or comprehensive request, the explicit matrix is the floor, not the ceiling. Treat each requested module or layer as an inventory heading, not one behavior: expand it into the bounded public operations and their distinct validation paths, branches, boundaries, interactions, and state transitions. After satisfying the explicit matrix, inspect each target API for observable equivalence partitions and invariants that the prompt did not name: identity, empty, singleton and representative interior inputs; exact boundaries plus an immediately adjacent value; invalid partitions; and ordering, monotonicity, rollover, capacity, truncation, or state invariants implied by the implementation. Add one mutation-relevant case per distinct partition not already proved, using parameterized or table-driven cases only for siblings that prove the same behavior. A passing coverage threshold is validation, not a breadth stop condition. Stop when remaining inputs exercise the same branch and invariant, not merely when the explicit checklist is complete; never add cases only to raise the count.

When to Use This Skill

Use this skill when you need to:

  • Generate unit tests for an entire project or specific files

  • Improve test coverage for existing codebases

  • Create test files that follow project conventions

  • Write tests that actually compile and pass

  • Add tests for new features or untested code

  • Generate or extend MSTest suites; load writing-mstest-tests as supporting guidance after this entry skill has established scope and project conventions

When Not to Use

  • Running or executing existing tests (use the run-tests skill)

  • Migrating between test frameworks (use migration skills)

  • Answering an MSTest API/pattern or modernization question that does not ask to generate tests (use writing-mstest-tests)

  • Analysis-only diagnosis of failing tests when no code or test changes are requested

How It Works

This skill coordinates multiple specialized agents in a Research → Plan → Implement pipeline:

Pipeline Overview
┌─────────────────────────────────────────────────────────────┐
│                     TEST GENERATOR                          │
│  Coordinates the full pipeline and manages state            │
└─────────────────────┬───────────────────────────────────────┘
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
┌───────────┐  ┌───────────┐  ┌───────────────┐
│ RESEARCHER│  │  PLANNER  │  │  IMPLEMENTER  │
│           │  │           │  │               │
│ Analyzes  │  │ Creates   │  │ Writes tests  │
│ codebase  │→ │ phased    │→ │ per phase     │
│           │  │ plan      │  │               │
└───────────┘  └───────────┘  └───────┬───────┘
                                      │
                    ┌─────────┬───────┼───────────┐
                    ▼         ▼       ▼           ▼
              ┌─────────┐ ┌───────┐ ┌───────┐ ┌───────┐
              │ BUILDER │ │TESTER │ │ FIXER │ │LINTER │
              │         │ │       │ │       │ │       │
              │ Compiles│ │ Runs  │ │ Fixes │ │Formats│
              │ code    │ │ tests │ │ errors│ │ code  │
              └─────────┘ └───────┘ └───────┘ └───────┘

Step-by-Step Instructions

Step 1: Determine the user request

Classify both intent and scope before editing:

  • Generate or extend: follow the generation workflow below.

  • Repair: reproduce the narrow failure, determine whether the defect is in the test or production behavior, make the smallest authorized fix, and rerun the covering command.

  • Audit then fix: invoke test-engineer so it can coordinate the internal quality specialist and implementation work.

Make sure you understand what user is asking and for what scope. When the user does not express strong requirements for test style, coverage goals, or conventions, source the guidelines from unit-test-generation.prompt.md. This prompt provides best practices for discovering conventions, parameterization strategies, behavior-focused coverage, and language-specific patterns.

Step 2: Size the request before invoking anything

Match the machinery to the scope. Running the full pipeline on a one-file request costs turns and tool calls without improving the tests.

ScopeWhat it looks likeHow to run it
FocusedOne function, class, or file; "tests for X only"; extending an existing suite with the missing casesSkip intermediate state files and the sub-agent fan-out. Keep the requirement checklist in your head (or in the final table), read only the target and one neighbouring test for conventions, write the tests, run the narrowest test command, review your own assertions inline.
BroadA project, package, or module set; "comprehensive suite"; a coverage threshold to clear across several filesRun the full Research → Plan → Implement pipeline in Step 3, with intermediate state files under <TESTAGENT_DIR> and the completion contract below.

When in doubt, start focused and escalate only if the request turns out to span several files. Escalating costs one extra pass; running the broad pipeline on a focused request costs several.

Before ending a focused request, check all three conditions together:

  1. every named behavior has a concrete assertion, including each requested boundary or error path;

  2. the narrow test command exited successfully;

  3. the final handoff maps those behaviors to exact test names and cites that successful command.

Do not replace requirement-level evidence with a generic list of covered areas.

Step 3: Invoke the Test Generator (broad scope)

Start by invoking the named test-engineer custom agent with your test generation request. Do not use a generic/general-purpose subagent merely named test-engineer:

You are the sole pipeline owner for this request. Do not invoke code-testing or another test-engineer; complete the phases in your current context. Generate unit tests for [path or description of what to test], following the [unit-test-generation.prompt.md](unit-test-generation.prompt.md) guidelines. Treat the current workspace as authoritative even when it is sparse, gutted-looking, synthetic, or missing tracked files; never restore or reconstruct it, including with `git checkout`, `git restore`, `git reset`, or `git clean`.

The Test Generator owns the pipeline. After it returns, consume its recorded quality checks, validation results, and requirement matrix instead of repeating Steps 4 and 5 as another pipeline. Do not reload review skills or rerun unchanged passing commands. Preserve exact test names from its evidence in the final handoff. If evidence is missing, inspect or follow up on that specific gap without restarting generation. A reported capability-wide denial also applies to the caller; do not attempt another command using that capability.

If test-engineer is unavailable, do not skip the workflow. Execute the same Research → Plan → Implement sequence inline, resolve <TESTAGENT_DIR> as described below, create the intermediate state files there, and apply the same completion contract.

For broad scope, resolve one absolute <TESTAGENT_DIR> before creating intermediate state files:

  1. Prefer a host-provided session artifact or scratch directory.

  2. Otherwise, in a Git worktree run git rev-parse --path-format=absolute --git-path testagent; this returns a path in worktree-specific Git metadata that cannot be staged.

  3. Outside Git, create a unique directory under the operating system's temporary directory.

Pass the absolute directory to every pipeline agent. The path may be inside the repository's .git metadata directory, but it must not be version-controlled workspace content, appear in git status, or be stageable.

Step 4: Execute with bounded context

For multi-file requests:

  1. Turn every explicit user requirement into a checklist before implementation. Include requested layers, collaborators to mock, boundary cases, integrations, coverage thresholds, and report artifacts. Copy multi-condition requirements verbatim — they must each map to one test that exercises the whole combination.

  2. Research only the requested module or project and write the checklist plus a compact target inventory to <TESTAGENT_DIR>/research.md.

  3. Reuse manifests, symbol references, and deterministic pairing tools instead of reading every source and test file.

  4. When an available find-untested-sources skill is useful for a substantial multi-file inventory, run it once and reuse its pairing and suggested-path output. Otherwise pair the bounded targets manually once; do not probe for an unavailable skill.

  5. Plan each target file once, then implement phases sequentially. Map every checklist item to at least one concrete test or explain why it is blocked.

  6. Build and test the narrow target during fix cycles. Run workspace-level validation once at the end only for broad work, when the repository contract requires that entry point, or when the changes can affect other projects.

  7. Before reporting success, re-open the generated tests and verify every checklist item against concrete test names and assertions. Coverage alone is not evidence that a requested mock seam, boundary, state transition, or property combination was tested.

  8. Read a language example from code-testing-extensions only when the repository has no representative tests and the base extension is insufficient.

  9. For .NET, classify SDK-style vs. classic non-SDK before choosing commands or creating files. In classic projects, preserve packages.config, existing framework/mock versions and custom base fixtures, add every new test file to the project's explicit <Compile Include> items, and use the repository's MSBuild/test-runner commands. Never modernize the project or dependency stack merely to generate tests.

  10. For MSTest, inspect the pinned package version before choosing exception assertions. MSTest 3.5.x uses Assert.ThrowsException<T>; do not substitute [ExpectedException], Assert.Throws<T>, or Assert.ThrowsExactly<T>.

Completion contract

Every scope must satisfy points 3–5 below. Points 1 and 2 are the broad-scope artifacts: on a focused request the same reasoning happens inline and no intermediate state files are written.

Do not report completion until all of these are true:

  1. (broad scope) <TESTAGENT_DIR>/research.md records the bounded target inventory, existing test conventions, and the acceptance checklist.

  2. (broad scope) <TESTAGENT_DIR>/plan.md maps each checklist item to a planned test or an explicit blocker.

  3. Generated tests compile and pass with the narrowest relevant test command, satisfying the shared report-safe naming and result-validation contract.

  4. Every explicit user requirement is backed by a concrete test and assertion. Fix missing mock seams, boundary cases, state transitions, and property combinations even when coverage already passes. In the final summary, cite at least one generated test name for every checklist item so completion is auditable; if an item has no test to cite, keep implementing or report it as blocked. For non-behavioral requirements such as scaffolding, scope limits, commands, or coverage artifacts, cite the relevant file, command, or report instead of forcing a test-name mapping. A passing suite with fewer tests is not automatically weaker: judge completeness by whether every independently requested behavior has direct, nonredundant evidence, not by raw test volume. For broad/comprehensive scope, also verify that every observable equivalence partition and invariant discovered in the bounded target APIs has one mutation-relevant case, even when the prompt did not name it. When the request names multiple modules, verify that each module's own non-trivial public behavior has direct test evidence in addition to any end-to-end composition test.

  5. Review the generated tests for behavior gaps and weak assertions. On a broad scope, invoke test-gap-analysis and assertion-quality when available and record the findings and fixes in <TESTAGENT_DIR>/status.md. On a focused scope, do the equivalent review inline — re-read each generated assertion against the source — without spawning extra passes.

The final response must provide requirement-by-requirement evidence. Use compact bullets under a Requirement coverage label for one to three focused requirements; use a Requirement | Evidence table for broader scopes. Behavioral evidence cites exact generated test names. Non-behavioral evidence cites the relevant project file, validation command, or coverage report. A generic list of tested areas is not a substitute.

Preserve the user's exact meaning in each evidence item; quote verbatim only when wording distinguishes a required combination. A test that merely exercises the same collaborators does not satisfy a requirement about their interaction, and per-class requirements need a citation per class.

Cite a clean run, not an attempt. The commands behind the final evidence must have finished successfully: quote the final passing test summary and, when thresholds were requested, the per-module coverage table from a run that exited 0. If the last coverage run exited non-zero, fix it and re-run before reporting; never infer threshold clearance from a failed or partial run.

Before reporting, inspect the final working-tree changes and confirm that research.md, plan.md, status.md, and any other intermediate state files are not among the changes intended for commit.

State Management

Broad-scope runs store intermediate state files in a non-stageable <TESTAGENT_DIR> backed by host scratch storage, Git metadata, or OS temp. A focused request does not create these files:

FilePurpose
<TESTAGENT_DIR>/research.mdCodebase analysis results
<TESTAGENT_DIR>/plan.mdPhased implementation plan
<TESTAGENT_DIR>/status.mdFinal quality review and fixes

Agent Reference

AgentPurpose
test-engineerCoordinates pipeline
code-testing-researcherAnalyzes codebase
code-testing-plannerCreates test plan
code-testing-implementerWrites test files
code-testing-builderCompiles code
code-testing-testerRuns tests
code-testing-fixerFixes errors
code-testing-linterFormats code

Requirements

  • Project must have a build/test system configured

  • Testing framework should be installed (or installable)

  • VS Code with GitHub Copilot extension

Classic non-SDK .NET projects are supported when their existing build/test toolchain is available. When it is not available on the current machine, the agent can still add and register version-compatible tests, but must report execution as blocked rather than substituting dotnet test.

Troubleshooting

Tests don't compile

The code-testing-fixer agent will attempt to resolve compilation errors. Check <TESTAGENT_DIR>/plan.md for the expected test structure. Read the matching language file relative to the supplied code-testing-extensions catalog for error code references (e.g., dotnet.md for .NET).

Tests fail

Most failures in generated tests are caused by wrong expected values in assertions, not production code bugs:

  1. Read the actual test output

  2. Read the production code to understand correct behavior

  3. Fix the assertion, not the production code

  4. Never mark tests [Ignore] or [Skip] just to make them pass

Wrong testing framework detected

Specify your preferred framework in the initial request: "Generate Jest tests for..."

Environment-dependent tests fail

Tests that depend on external services, network endpoints, specific ports, or precise timing will fail in CI environments. Focus on unit tests with mocked dependencies instead.

Broader validation fails

During implementation, build and test the narrow target. Run a solution or workspace-level command only for broad work, when the repository contract uses that entry point, or when the targeted change can affect other projects. Do not turn a focused test request into an unconditional full non-incremental build.

Files

unit-test-generation.prompt.md


description: >- Best practices for generating proportional, behavior-focused, parameterized unit tests across programming languages

Unit Test Generation Prompt

You are an expert code generation assistant specialized in writing concise, effective unit tests. Analyze the requested source scope, identify meaningful behavior partitions and plausible bugs, and produce minimal, buildable tests. Treat a coverage percentage as a requirement only when the user or repository specifies one; do not chase an arbitrary 80% target.

Discover and Follow Conventions

Before generating tests, analyze the codebase to understand existing conventions:

  • Location: Where test projects and test files are placed

  • Naming: Namespace, class, and method naming patterns

  • Frameworks: Testing, mocking, and assertion frameworks used

  • Harnesses: Preexisting setups, base classes, or testing utilities

  • Guidelines: Testing or coding guidelines in instruction files, README, or docs

If you identify a strong pattern, follow it unless the user explicitly requests otherwise. If no pattern exists and there's no user guidance, use your best judgment.

Test Generation Requirements

Generate concise, parameterized, and effective unit tests using discovered conventions.

  • Prefer mocking over generating one-off testing types

  • Prefer unit tests over integration tests, unless integration tests are clearly needed and can run locally

  • Focused scope: inspect the named target, its direct collaborators, and one representative neighboring test for conventions

  • Broad scope: inventory the requested modules first, then cover their non-trivial public behavior without reading unrelated code. A module or layer name is an inventory heading, not one test requirement: enumerate its public operations and distinct validation, boundary, branch, interaction, and state behavior before deciding it is covered

  • Stop only when every requested behavior and distinct observable partition has a mutation-relevant assertion and any requested coverage target is met

Key Testing Goals
GoalDescription
Minimal but ComprehensiveAvoid redundant tests
Logical CoverageFocus on meaningful edge cases, domain-specific inputs, boundary values, and bug-revealing scenarios
Core Logic FocusTest positive cases and actual execution logic; avoid low-value tests for language features
Balanced CoverageDon't let negative/edge cases outnumber tests of actual logic
Best PracticesUse Arrange-Act-Assert pattern and proper naming (Method_Condition_ExpectedResult)
Buildable & CompleteTests must compile, run, and contain no hallucinated or missed logic

Quality over Quantity

When the task specifies particular test scenarios or behaviors to cover:

  1. Cover every stated requirement first — each bullet point or scenario in the task description should map to at least one test

  2. Test the actual implementation — read the source code to understand return values, side effects, and error conditions before writing assertions

  3. Keep focused suites concise without shrinking broad suites — for one function, 5 tests that thoroughly exercise its distinct behavior beat 20 shallow tests. For broad/comprehensive work, do not optimize for fewer tests: combine only equivalent sibling inputs, never separate public behaviors, validation paths, boundaries, or state transitions merely because coverage already passes

  4. Every test must pass — run tests after writing them; fix immediately if they fail

  5. Make completion auditable — before finishing, cite at least one generated test name for every explicit behavioral requirement. For scaffolding, scope, commands, and coverage requirements, cite the relevant file or artifact. Passing coverage is not completion when a requested mock seam, boundary, transition, or property combination has no mapped test.

Write Tests That Pin Down Behavior

A test that passes coincidentally gives a false signal. Beyond covering code, every test must pin down behavior — it should fail under a plausible bug. These principles are language-agnostic (MSTest, xUnit, NUnit, pytest, Jest, Go testing, JUnit, RSpec, ...):

  • Mutation thinking — each assertion should fail under at least one plausible mutation (>→>=, &&→||, a dropped null/None/nil check, an off-by-one, returning the input unchanged). If it survives every mutation, replace weak checks (IsNotNull/toBeDefined) with a concrete expected value.

  • No tautologies — do not compare a value with itself or derive the expected value from the actual result. A write/read assertion is valid when persistence or round-tripping is the contract and the expected value is independently specified.

  • Property intersections — when code handles independent properties (quoted/unquoted, ASCII/escaped, present/absent), add at least one test combining several at once. Bugs live at intersections, not on single axes.

  • Behavior radius — assert a secondary observable only when it is part of the public contract or required to prove the requested interaction; do not couple every test to incidental state, logs, or call counts.

  • Fixture realism — never set the parameter under test to a degenerate value (scroll with scrollback=0, eviction with capacity=1, retries with maxRetries=0, ordering with a single element).

Quick self-review before finishing a test: would emptying the function body make it fail? If not, the assertions are too weak.

Parameterization

  • Prefer parameterized tests (e.g., [DataRow], [Theory], @pytest.mark.parametrize) over multiple similar methods

  • Combine logically related test cases into a single parameterized method

  • Never generate multiple tests with identical logic that differ only by input values

Report-safe test names and result validation

Apply this contract to both direct generation and delegated implementation or validation, even when the caller supplies its own test style.

  • Separate metadata from data. Give each case a stable, descriptive, distinguishable ID/display name. Prefer an explicit safe Name/Case field; a case index plus a short behavior label also works. Do not interpolate arbitrary input, expected values, or output into test or suite names.

  • Keep raw control characters, isolated UTF-16 surrogates, binary values, and huge strings out of names. If an identifier needs an escaped value, use a short literal backslash-u label such as \u000C (six printable characters), not the actual control character. Normal Unicode labels and harmless numeric interpolation are fine.

  • Check framework-managed parameterized labels too: when automatic argument rendering would expose unsafe data, use the framework's explicit case-ID or display-name API (for example, pytest ids or MSTest DisplayName) rather than assuming the runner/reporter escapes it safely.

  • Preserve the case. Control characters and malformed strings are legitimate test data. Fix unsafe metadata, not production values or expectations; never sanitize the tested data, weaken assertions, or skip/remove edge cases to make a report export succeed.

Before claiming tests passed:

  1. Use the repository/CI configured runner and reporter at the narrowest scope covering the change. Preserve the runner exit code through wrappers/pipelines; a successful log-filter command is not a successful test run.

  2. When result artifacts are required or configured, run the real report-export path and parse the artifacts from that run with the existing consumer or an appropriate format parser (for example, an XML parser for JUnit/TRX). Console-green alone is insufficient if required export failed.

  3. Confirm nonzero expected discovery and account for every discovered case's pass/skip/failure outcome, including setup failures or incomplete execution. Reject missing, empty, invalid, stale, or partial required artifacts; do not infer success from an empty report or a summary that omits failures.

  4. Report runner, export, and parsing failures explicitly with the command, exit code, artifact path, and diagnostic; keep completion blocked until required validation succeeds. Do not add reporter dependencies, a new report format, coverage collection, or a full-suite rerun merely for naming checks when reporting is not configured.

Analysis Before Generation

Do this analysis privately; do not emit a plan or inventory unless the user requested one. For focused work, stop gathering context once the target behavior, expected results, dependencies, and local test conventions are known. For broad work, inventory manifests and symbols first, batch independent file reads where tools allow, and stop when every requested target has a test location and behavior checklist.

Before writing tests:

  1. Analyze the code line by line to understand what each section does

  2. Document all parameters, their purposes, constraints, and valid/invalid ranges

  3. Identify potential edge cases and error conditions

  4. Describe expected behavior under different input conditions

  5. Note dependencies that need mocking

  6. Consider concurrency, resource management, or special conditions

  7. Identify domain-specific validation or business rules

Apply this analysis to the requested scope, not adjacent modules.

Coverage Types

TypeExamples
Happy PathValid inputs produce expected outputs
Edge CasesEmpty values, boundaries, special characters, zero/negative numbers
Error CasesInvalid inputs, null handling, exceptions, timeouts
State TransitionsBefore/after operations, initialization, cleanup

Language-Specific Examples

C# (MSTest)
[TestClass]
public sealed class CalculatorTests
{
    private readonly Calculator _sut = new();

    [TestMethod]
    [DataRow(2, 3, 5, DisplayName = "Positive numbers")]
    [DataRow(-1, 1, 0, DisplayName = "Negative and positive")]
    [DataRow(0, 0, 0, DisplayName = "Zeros")]
    public void Add_ValidInputs_ReturnsSum(int a, int b, int expected)
    {
        // Act
        var result = _sut.Add(a, b);

        // Assert
        Assert.AreEqual(expected, result);
    }

    [TestMethod]
    public void Divide_ByZero_ThrowsDivideByZeroException()
    {
        // Act & Assert
        Assert.ThrowsException<DivideByZeroException>(() => _sut.Divide(10, 0));
    }
}
TypeScript (Jest)
describe("Calculator", () => {
  let sut: Calculator;

  beforeEach(() => {
    sut = new Calculator();
  });

  it.each([
    [2, 3, 5],
    [-1, 1, 0],
    [0, 0, 0],
  ])("add(%i, %i) returns %i", (a, b, expected) => {
    expect(sut.add(a, b)).toBe(expected);
  });

  it("divide by zero throws error", () => {
    expect(() => sut.divide(10, 0)).toThrow("Division by zero");
  });
});
Python (pytest)
import pytest
from calculator import Calculator

class TestCalculator:
    @pytest.fixture
    def sut(self):
        return Calculator()

    @pytest.mark.parametrize("a,b,expected", [
        (2, 3, 5),
        (-1, 1, 0),
        (0, 0, 0),
    ])
    def test_add_valid_inputs_returns_sum(self, sut, a, b, expected):
        assert sut.add(a, b) == expected

    def test_divide_by_zero_raises_error(self, sut):
        with pytest.raises(ZeroDivisionError):
            sut.divide(10, 0)

Output Requirements

  • Tests must be complete and buildable with no placeholder code

  • Follow the exact conventions discovered in the target codebase

  • Include appropriate imports and setup code

  • Add brief comments explaining non-obvious test purposes

  • Place tests in the correct location following project structure

Build and Verification

  • Scoped builds during development: Build the specific test project during implementation for faster iteration

  • Final validation: Run the narrowest command that compiles and executes the changed tests. Add a solution/workspace command only for broad work, when the repository contract requires it, or when the change can affect other projects.

  • API signature verification: Before calling any method in test code, verify the exact parameter types, count, and order by reading the source code

  • Project reference validation: Before writing test code, verify the test project references all source projects the tests will use. Read the matching language file relative to the supplied code-testing-extensions catalog (e.g., dotnet.md for .NET); do not invoke the reference-only catalog.

Test Scope Guidelines

  • Write unit tests, not integration/acceptance tests: Focus on testing individual classes and methods with mocked dependencies

  • No external dependencies: Never write tests that call external URLs, bind to network ports, require service discovery, or depend on precise timing

  • Mock everything external: HTTP clients, database connections, file systems, network endpoints — all should be mocked in unit tests

  • Fix assertions, not production code: When tests fail, read the production code, understand its actual behavior, and update the test assertion

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
.NET (Microsoft)
Publisher
.NET (Microsoft) (Official)
Source
dotnet/skills · plugins/dotnet-test/skills/code-testing · commit 96c3d68
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by .NET (Microsoft).

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download code-testing.zip