Skip to content

Testing Overview

Every module generated by Modulus includes three dedicated test projects. The scaffolded test projects come pre-configured with the right packages, a clean folder structure, and a baseline set of architecture tests -- you can start writing tests immediately after running modulus add-module.

Test Project Types

ProjectSuffixPurpose
Unit TestsTests.UnitTest handlers, validators, and domain logic in isolation
Integration TestsTests.IntegrationTest endpoints and data access with real infrastructure
Architecture TestsTests.ArchitectureEnforce layer dependency rules and coding conventions

Project Naming Convention

Test projects are named {ModuleName}.Tests.{Type} (namespaces are {SolutionName}.{ModuleName}.Tests.{Type}) and live inside the module's own directory:

src/Modules/Catalog/tests/
├── Catalog.Tests.Unit/
├── Catalog.Tests.Integration/
└── Catalog.Tests.Architecture/

They are grouped under /tests/Modules/Catalog/ in the solution file. The solution root's tests/ directory holds the solution-level projects scaffolded by modulus init ({Solution}.Tests.Common, {Solution}.Tests.Architecture, {Solution}.Tests.Integration).

Packages

All test projects are scaffolded with the following packages:

PackagePurpose
xunitTest framework
xunit.runner.visualstudioVisual Studio / CLI test runner
Microsoft.NET.Test.Sdk.NET test host
ShouldlyFluent assertion library

Unit test projects additionally include:

PackagePurpose
FluentValidationValidator testing (TestValidate)

Architecture test projects additionally include:

PackagePurpose
NetArchTest.RulesFluent API for enforcing architectural constraints

Integration test projects additionally include:

PackagePurpose
Microsoft.AspNetCore.Mvc.TestingWebApplicationFactory for hosting the app in tests
Microsoft.EntityFrameworkCore.InMemoryIn-memory EF Core provider for lightweight test doubles
Testcontainers.MsSqlDocker-based SQL Server for test isolation
ModulusKit.TestingMessaging test harness, test transport, and outbox/inbox assertion helpers -- see ModulusKit.Testing

No mocking framework is scaffolded -- the generated tests exercise real objects. If your team prefers mocks, add NSubstitute or Moq to the unit test project yourself (see Unit Testing). For messaging, prefer ModulusKit.Testing's TestMessageTransport and ModulusMessagingTestHarness over hand-rolling a fake IMessageTransport or writing raw OutboxDbContext/InboxDbContext queries -- see ModulusKit.Testing.

Running Tests

Run all tests from the solution root:

bash
dotnet test

Run tests for a specific module:

bash
dotnet test src/Modules/Catalog/tests/Catalog.Tests.Unit

Run only architecture tests:

bash
dotnet test --filter "FullyQualifiedName~Tests.Architecture"

Run only unit tests:

bash
dotnet test --filter "FullyQualifiedName~Tests.Unit"

Run tests in CI

The scaffold does not generate a CI workflow -- add dotnet test to your pipeline of choice so architecture tests catch dependency violations before they reach production.

Test Organization

Tests within each project are organized to mirror the module's structure. The CLI scaffolders follow this convention too -- add-command, add-query, and add-entity each drop a starter test into the matching folder:

Catalog.Tests.Unit/
├── Commands/
│   └── CreateProductHandlerTests.cs
├── Queries/
│   └── GetProductByIdHandlerTests.cs
└── Domain/
    └── ProductTests.cs

This convention makes it easy to find the test for any given class. The test class name mirrors the class under test with a Tests suffix.

What's Next

Dive into the specific testing guides:

  • Architecture Tests -- Enforce layer dependency rules with NetArchTest and Roslyn analyzers
  • Unit Testing -- Test handlers, validators, and domain logic in isolation
  • Integration Testing -- Test endpoints end-to-end with WebApplicationFactory
  • ModulusKit.Testing -- Test messaging module-to-module with a real pipeline instead of hand-rolled fakes

Released under the MIT License.