# Testing Patterns

> Unit testing patterns with xUnit, Moq, and FluentAssertions including AAA pattern, naming conventions, mocking, and edge case coverage. Use when writing or reviewing unit tests.

- Skill: `joshua-palamuttam/testing-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add joshua-palamuttam/testing-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/joshua-palamuttam/testing-patterns/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Joshua-Palamuttam (https://skillmd.com/u/joshua-palamuttam)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/joshua-palamuttam/testing-patterns

---


# Testing Patterns

## Overview
Tests ensure code correctness and enable confident refactoring. Follow these patterns for consistent, maintainable tests.

## Test Framework Stack
- **xUnit** - Test framework
- **Moq** - Mocking library
- **FluentAssertions** - Readable assertions

## Test Structure

### Arrange-Act-Assert (AAA)
Every test follows this pattern:

```csharp
[Fact]
public async Task GetByIdAsync_WithValidId_ReturnsTask()
{
    // Arrange
    var taskId = Guid.NewGuid();
    var expectedTask = new TaskItem { Id = taskId, Title = "Test Task" };
    _repositoryMock.Setup(r => r.GetByIdAsync(taskId, It.IsAny<CancellationToken>()))
        .ReturnsAsync(expectedTask);

    // Act
    var result = await _sut.GetByIdAsync(taskId);

    // Assert
    result.Should().NotBeNull();
    result!.Id.Should().Be(taskId);
    result.Title.Should().Be("Test Task");
}
```

### Test Class Structure
```csharp
public class TaskServiceTests
{
    private readonly Mock<ITaskRepository> _repositoryMock;
    private readonly Mock<ILogger<TaskService>> _loggerMock;
    private readonly TaskService _sut; // System Under Test

    public TaskServiceTests()
    {
        _repositoryMock = new Mock<ITaskRepository>();
        _loggerMock = new Mock<ILogger<TaskService>>();
        _sut = new TaskService(_repositoryMock.Object, _loggerMock.Object);
    }

    // Tests...
}
```

## Naming Conventions

### Test Method Names
Format: `MethodName_Scenario_ExpectedResult`

```csharp
// Good - descriptive names
public async Task GetByIdAsync_WithValidId_ReturnsTask()
public async Task GetByIdAsync_WithNonExistentId_ReturnsNull()
public async Task CreateAsync_WithValidRequest_CreatesAndReturnsTask()
public async Task DeleteAsync_WithExistingId_ReturnsTrue()

// Bad - unclear names
public async Task TestGet()
public async Task Test1()
```

## Mocking with Moq

### Setup Mock Returns
```csharp
// Return a value
_repositoryMock.Setup(r => r.GetByIdAsync(taskId, It.IsAny<CancellationToken>()))
    .ReturnsAsync(expectedTask);

// Return null
_repositoryMock.Setup(r => r.GetByIdAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
    .ReturnsAsync((TaskItem?)null);
```

### Verify Mock Calls
```csharp
_repositoryMock.Verify(r => r.CreateAsync(It.IsAny<TaskItem>(), It.IsAny<CancellationToken>()), Times.Once);
_repositoryMock.Verify(r => r.DeleteAsync(taskId, It.IsAny<CancellationToken>()), Times.Once);
```

## FluentAssertions

### Basic Assertions
```csharp
result.Should().Be(expected);
result.Should().BeNull();
result.Should().NotBeNull();
items.Should().HaveCount(3);
items.Should().BeEmpty();
result.IsCompleted.Should().BeTrue();
```

## Edge Case Coverage

### Always Test These Scenarios
- GET: found, not found, empty list
- CREATE: success, validates input, sets defaults
- UPDATE: success, not found, partial update
- DELETE: success, not found

