> For the complete documentation index, see [llms.txt](https://framework.aic.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://framework.aic.io/technical-guidelines-code-standards-and-tech-stack/tech-stack/testing-stack.md).

# Testing Stack

## AIC Standard

NUnit is the default automated testing framework for AIC .NET solutions.

Moq is the default mocking library for unit tests and interaction-based tests.

All tests must run through `dotnet test` and must be suitable for local execution and CI execution.

## Default Testing Toolchain

| Area                      | Default choice                                      | Notes                                                |
| ------------------------- | --------------------------------------------------- | ---------------------------------------------------- |
| Test framework            | NUnit                                               | Default for unit, integration and component tests.   |
| Test runner               | dotnet test                                         | Required for CI compatibility.                       |
| Mocking                   | Moq                                                 | Default mocking library.                             |
| Assertions                | NUnit assertions or FluentAssertions where approved | Keep assertions readable and behaviour-focused.      |
| Test data                 | Builders, fixtures and deterministic factories      | Avoid fragile shared test state.                     |
| MongoDB integration tests | Testcontainers or controlled test database          | Prefer disposable or isolated databases.             |
| Redis integration tests   | Testcontainers or controlled test Redis instance    | Never rely on shared mutable cache state.            |
| Coverage                  | Coverlet or CI-native coverage                      | Coverage informs quality; it does not prove quality. |
| Mutation testing          | Stryker.NET where justified                         | Useful for critical domain logic.                    |

## NUnit Standards

* Use `[TestFixture]` for fixture classes where it improves clarity.
* Use `[Test]` for single behaviour tests.
* Use `[TestCase]` for concise parameterised examples.
* Use `[SetUp]` sparingly; prefer explicit test builders when setup is important to understand.
* Avoid hidden shared mutable state across tests.
* Test names must describe the behaviour being verified.
* Tests must be deterministic.
* Tests must not depend on execution order.
* Tests must pass when run individually and as a suite.

## Test Naming Standard

Use behaviour-first names.

Preferred:

```csharp
[Test]
public async Task CreateCaseAsync_WhenRequestIsValid_PersistsCaseAndReturnsIdentifier()
{
    // Arrange

    // Act

    // Assert
}
```

Avoid vague names:

```csharp
[Test]
public async Task TestCreateCase()
{
}
```

## Moq Standards

Use Moq to isolate behaviours at clear architectural boundaries.

Good uses:

* mocking external gateways
* mocking notification senders
* mocking clocks
* mocking identity providers
* mocking repositories in application service unit tests
* verifying an important interaction where the interaction is the behaviour

Poor uses:

* mocking every class by habit
* mocking simple domain objects
* verifying implementation details
* creating tests that break during harmless refactoring
* using mocks instead of proper integration tests for persistence or API boundaries

Example:

```csharp
[Test]
public async Task SendWelcomeEmailAsync_WhenUserExists_SendsEmailToUser()
{
    Mock<IUserRepository> userRepositoryMock = new();
    Mock<IEmailSender> emailSenderMock = new();

    UserRecord userRecord = UserRecordSource.GetDefault();

    userRepositoryMock
        .Setup(userRepository => userRepository.GetByIdAsync(userRecord.Id, It.IsAny<CancellationToken>()))
        .ReturnsAsync(userRecord);

    WelcomeEmailService welcomeEmailService = new(
        userRepositoryMock.Object,
        emailSenderMock.Object);

    await welcomeEmailService.SendWelcomeEmailAsync(userRecord.Id, CancellationToken.None);

    emailSenderMock.Verify(
        emailSender => emailSender.SendAsync(
            userRecord.EmailAddress,
            It.IsAny<string>(),
            It.IsAny<CancellationToken>()),
        Times.Once);
}
```

## Test Project Naming

Use clear project names:

* `Aic.Product.UnitTests`
* `Aic.Product.IntegrationTests`
* `Aic.Product.ContractTests`
* `Aic.Product.EndToEndTests`

## Required Practices

* Unit tests must be fast and deterministic.
* Integration tests must clearly state external dependencies.
* MongoDB and Redis tests must isolate data by database, collection, key prefix or container.
* Tests must not use production credentials.
* Tests must not depend on real customer data.
* Critical domain logic must be covered by direct tests.
* Bug fixes should include regression tests.
* CI must fail when required tests fail.

## Quality Gate

Before release or handover, confirm:

* NUnit tests run successfully through `dotnet test`
* unit tests cover expected business behaviours
* integration tests cover MongoDB persistence paths where relevant
* Redis caching behaviour is tested where relevant
* Moq usage is focused on boundaries, not implementation noise
* failure and edge cases are tested
* security-sensitive behaviour is tested
* test evidence is retained in CI
