writing-unit-tests-csharp

SKILL.md

Skill: Writing C# Unit Tests

Overview

This skill covers writing unit tests for the backend using MSTest SDK. The project uses a lean, zero-config approach that leverages Microsoft's official SDK-style testing.

Project Structure

backend/
├── WebApp.sln
├── WebApp.Api/
│   ├── Models/           # DTOs and request/response models
│   ├── Services/         # Business logic and agent integration
│   └── Program.cs        # Minimal API endpoints
└── WebApp.Api.Tests/
    ├── WebApp.Api.Tests.csproj
    └── [TestClass].cs    # Test files organized by class under test

Test Project Configuration

The test project uses MSTest SDK which eliminates the need for explicit package references:

<Project Sdk="MSTest.Sdk/3.10.2">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\WebApp.Api\WebApp.Api.csproj" />
  </ItemGroup>
</Project>

Test Anatomy

using WebApp.Api.Models;

namespace WebApp.Api.Tests;

[TestClass]
public class ErrorResponseFactoryTests
{
    [TestMethod]
    public void CreateFromException_ReturnsCorrectStatusCode()
    {
        // Arrange
        var exception = new InvalidOperationException("Test");
        
        // Act
        var result = ErrorResponseFactory.CreateFromException(exception, 500, isDevelopment: false);
        
        // Assert
        Assert.AreEqual(500, result.Status);
    }
    
    [TestMethod]
    [DataRow(400, "Bad Request")]
    [DataRow(401, "Unauthorized")]
    [DataRow(500, "Internal Server Error")]
    public void CreateFromException_MapsStatusToTitle(int status, string expectedTitle)
    {
        // Parameterized test using DataRow
    }
}

Running Tests

# Run all backend tests
cd backend
dotnet test

# Run with verbose output
dotnet test --verbosity normal

# Run specific test class
dotnet test --filter "FullyQualifiedName~ErrorResponseFactoryTests"

# Run with coverage (requires coverlet)
dotnet test --collect:"XPlat Code Coverage"

Testable Units in This Project

Models (Pure, Easy to Test)

Class What to Test
ErrorResponseFactory Status code mapping, dev vs prod mode, exception detail hiding
ChatRequest Validation logic if any
StreamChunk Serialization/deserialization
AnnotationInfo Property mapping

Services (Require Integration Testing)

Class Testing Approach
AgentFrameworkService Use validating-ui-features skill with Playwright for integration tests

Test Naming Convention

Use descriptive names following: MethodName_Scenario_ExpectedBehavior

[TestMethod]
public void CreateFromException_WhenDevelopmentMode_IncludesStackTrace() { }

[TestMethod]
public void CreateFromException_WhenProductionMode_HidesStackTrace() { }

Assertions Reference

MSTest provides these assertion methods:

// Equality
Assert.AreEqual(expected, actual);
Assert.AreNotEqual(notExpected, actual);

// Nullability
Assert.IsNull(value);
Assert.IsNotNull(value);

// Boolean
Assert.IsTrue(condition);
Assert.IsFalse(condition);

// Type
Assert.IsInstanceOfType(obj, typeof(ExpectedType));

// Collections
CollectionAssert.Contains(collection, element);
CollectionAssert.AreEqual(expected, actual);

// Exceptions
Assert.ThrowsException<InvalidOperationException>(() => MethodThatThrows());

When Unit Tests Aren't Enough

Use the validating-ui-features skill and Playwright when:

  • Testing requires a running backend server
  • Testing SSE streaming behavior
  • Testing authentication flows
  • Testing the full chat request/response cycle

Quick Reference

Command Purpose
dotnet test Run all tests
dotnet test --filter "Name~Test" Run filtered tests
dotnet build Build without running
dotnet test --list-tests List all tests
Weekly Installs
1
GitHub Stars
66
First Seen
Mar 1, 2026
Installed on
amp1
cline1
opencode1
cursor1
continue1
kimi-cli1