Claude
Skills
Sign in
Back

dotnet-unittest

Included with Lifetime
$97 forever

Provides guidance for generating comprehensive C# unit tests in .NET projects. Use when writing, scaffolding, or improving unit tests for C# code using MSTest, NUnit, or xUnit frameworks. Covers framework detection, test structure, edge-case analysis, mocking rules, and best practices. DO NOT use for integration tests, end-to-end tests, or non-.NET languages.

Writing & Docs

What this skill does


# .NET Unit Test Generation

Guidance for producing high-quality, comprehensive C# unit tests in .NET projects. This skill covers how to detect the test framework in use (MSTest, NUnit, or xUnit) and how to structure well-formed tests that follow best practices, cover edge cases, and avoid common pitfalls.

## When to Use

- Writing unit tests for new or existing C# code
- Scaffolding a test class for a production class
- Improving test coverage with edge-case and error-condition tests
- Generating parameterized tests for methods with varied inputs

## When Not to Use

- The target file contains only interfaces or delegates (no testable logic)
- Writing integration tests or end-to-end tests (different patterns apply)
- The project is not a .NET / C# project

## Inputs

| Input | Required | Description |
|-------|----------|-------------|
| Source code to test | Yes | The C# class, method, or file that needs tests |
| Existing test project | Recommended | Helps detect the test framework and coding conventions |

## Workflow

### Step 1: Detect the test framework

Check the project for existing test framework references:

1. Look for NuGet package references in `.csproj` files
2. Scan existing test files for framework-specific attributes
3. Match against known frameworks:
   - **MSTest**: `MSTest` or `Microsoft.VisualStudio.TestTools.UnitTesting` package
   - **NUnit**: `NUnit` package, `NUnit3TestAdapter`
   - **xUnit**: `xunit` package, `xunit.runner.visualstudio`
4. If no framework is detected, ask the user which framework to use

### Step 2: Load framework-specific guidance

Based on the detected framework, load the corresponding reference:

- MSTest → [references/mstest.md](references/mstest.md)
- NUnit → [references/nunit.md](references/nunit.md)
- xUnit → [references/xunit.md](references/xunit.md)

### Step 3: Analyze the source code

Before generating tests, analyze the code thoroughly:

1. Read the source code line by line, understanding what each section does
2. Document all parameters, their purposes, constraints, and valid/invalid value ranges
3. Identify potential edge cases and error conditions based on implementation details
4. Describe the expected behavior of each method under different input conditions
5. Simulate an execution flow to understand how different inputs affect the code path
6. Note any dependencies, interfaces, or external systems that will need to be mocked
7. Consider how concurrency, resource management, or other special conditions might affect behavior
8. Identify any domain-specific validation or business rules that need testing

### Step 4: Generate the test class

Apply the framework-specific conventions from Step 2 and the common guidelines below. Produce a complete, compilable test file.

### Step 5: Validate

- [ ] Test file compiles without errors
- [ ] All using directives are present and sorted alphabetically
- [ ] Test class and methods follow naming conventions
- [ ] Framework-specific attributes are applied correctly
- [ ] Arrange-Act-Assert pattern is clearly followed
- [ ] Edge cases and error conditions are covered
- [ ] No fake/stub/dummy classes were created — only mocking framework is used
- [ ] Code respects the project's C# language version and nullable reference type settings

## Key Testing Goals

- **Minimal but Comprehensive**: Avoid redundant tests. Prefer parameterized tests over duplicate methods.
- **Logical Coverage and Bug Detection**: Focus primarily on meaningful edge cases, domain-specific inputs, boundary values, and scenarios likely to reveal functional bugs or unexpected behavior.
- **Clarity and Best Practices**: Clearly use the Arrange-Act-Assert pattern, proper naming conventions (`Method_Condition_ExpectedResult`), and XML comments explaining test purposes.

## When NOT to Generate Tests

If the provided file or scope only contains interfaces or delegates, do not generate tests.

## Minimizing Hallucinations and Improving Accuracy

- NEVER invent or assume behaviors, methods, constructors, or properties that are not explicitly present in the provided source code.
- If you encounter incomplete or unclear source code or design, explicitly generate a partial test method with explanatory comments guiding users on how to proceed. Prefer this approach over generating incorrect or invalid test code.
- NEVER add additional types other than test classes — no dummy, fake, or stub classes. Use mocking instead. The only exception is a helper class inside the test class or production type overrides for exposing protected members. Even then, never place those outside the test class.

## Test Class Structure

For each public, protected, or internal class in the provided scope:

- Create a corresponding test class named `[ClassName]Tests`
- Place the test class in namespace `[SourceNamespace].UnitTests`
- If the tested source class is partial, the test class must also be partial
- Follow the initialization pattern that matches the detected test framework (see framework references)

## Test Method Conventions

### Naming

- Use meaningful, descriptive, and standardized naming: `MethodName_Condition_ExpectedOutcome`
- Follow the existing test naming conventions found in the project
- Each test method must include a clear XML documentation comment explaining:
  - The test purpose
  - The specific input conditions being tested
  - The expected result or exception

### Structure

- Clearly follow the Arrange-Act-Assert (AAA) pattern
- Keep tests focused on a single behavior
- Avoid testing multiple behaviors in one test method
- Avoid using multiple assertions in one test method — prefer multiple focused tests
- When testing multiple preconditions, write a separate test for each
- When testing multiple outcomes for one precondition, use parameterized tests
- Tests should be able to run in any order or in parallel

## Input and Edge Case Analysis

For **every parameter type**, ensure the following edge cases are tested:

### Numeric Parameters
- `int.MinValue`, `int.MaxValue`, `0`, negative and positive boundary values
- For floating-point: `double.NaN`, `double.PositiveInfinity`, `double.NegativeInfinity`

### String Parameters
- `null` (if nullable), empty strings (`""`), whitespace-only strings
- Very long strings, strings with special/control/invalid characters

### Domain-Specific Parameters
- Identify and test invalid, boundary, and special-case values based on parameter names and context
- Examples: for `"path"` → invalid file paths; for `"age"` → negative, zero, unreasonably large; for `"email"` → invalid formats

### Nullable Parameters
- Explicitly test `null` inputs
- Strictly follow the source code's nullability annotations

### Collections and Arrays
- `null` (if nullable), empty, single-item, duplicates, invalid elements

### Enums
- Test all defined values
- Test values outside the defined range (using casting) if possible

## Exception and Error Condition Testing

For all methods, explicitly test scenarios expected to throw exceptions:

- Invalid arguments, `null` values for non-nullable parameters, out-of-range values, invalid state
- Ensure the correct exception type is validated
- Where possible, validate the exception message
- If the method is not expected to throw, verify that no exception is thrown for valid inputs
- Use framework-specific async exception assertion methods for async code

## Dependency Handling and Mocking

### Strict Prohibition on Custom Implementations

- **STRICT PROHIBITION**: DO NOT create any custom, fake, stub, or dummy classes for dependencies under any circumstances
  - DO NOT create stub classes (e.g., `StubLogger`, `StubRepository`)
  - DO NOT create dummy classes (e.g., `DummyService`, `DummyContext`)
  - DO NOT create fake types (e.g., `FakeDatabase`, `FakeDependency`)
  - DO NOT create test-specific implementations of interfaces or abstract classes
- NEVER create a fake or custom implementation for abstract classes — all major .NET mocking fra

Related in Writing & Docs