Overview
Silky provides integration testing infrastructure through the Silky.TestBase module. Unlike pure unit tests, integration tests start a real Autofac IoC container, load module dependencies, and execute code in a context close to the real runtime — verifying DI, configuration loading, and module initialization.
Installation
<PackageReference Include="Silky.TestBase" Version="3.9.2" />
<PackageReference Include="xunit" Version="2.x.x" />
<PackageReference Include="Shouldly" Version="4.x.x" /> <!-- optional assertion library -->
Core Type: SilkyIntegratedTest<TStartupModule>
SilkyIntegratedTest<TStartupModule> is the base class for all integration test classes. The generic parameter TStartupModule is the test-specific module entry point.
On construction, the base class:
- Creates a host environment (default name:
Testing) - Reads config files (
appsettings.json,appsettings.Testing.json, etc.) - Builds the Autofac container and runs full module initialization
- Exposes
ServiceProvider,Configuration,Engine, and other properties
| Member | Description |
|---|---|
ServiceProvider | Root service container (IServiceProvider) |
TestServiceScope | Test service scope (IServiceScope) |
Configuration | Test environment IConfiguration |
Engine | Silky engine instance (IEngine) |
GetRequiredService<T>() | Resolve a service from the test scope |
BeforeAddApplication(services) | Hook: add registrations before module init |
AfterAddApplication(services) | Hook: add registrations after module init |
CreateConfigurationBuilder() | Hook: customize configuration building |
CreateHostEnvironment() | Hook: customize the test host environment |
On test class disposal, Dispose() automatically closes TestServiceScope and releases all Scoped services.
Basic Usage
Step 1: Create a Test Module
using Silky.Core.Modularity;
[DependsOn(
typeof(OrderApplicationModule),
typeof(AccountApplicationModule)
)]
public class OrderTestModule : SilkyModule
{
// Override ConfigureServices or Initialize for test-specific setup
}
Step 2: Create a Test Class
using Silky.TestBase;
using Shouldly;
using Xunit;
public class OrderAppServiceTests : SilkyIntegratedTest<OrderTestModule>
{
private readonly IOrderAppService _orderAppService;
public OrderAppServiceTests()
{
_orderAppService = GetRequiredService<IOrderAppService>();
}
[Fact]
public async Task CreateOrder_WithValidInput_ShouldSucceed()
{
// Arrange
var input = new CreateOrderInput
{
ProductId = 1,
Quantity = 2,
Address = "123 Main St"
};
// Act
var result = await _orderAppService.CreateOrderAsync(input);
// Assert
result.ShouldNotBeNull();
result.Id.ShouldBeGreaterThan(0);
}
[Fact]
public async Task GetOrder_WithInvalidId_ShouldThrow()
{
await Should.ThrowAsync<EntityNotFoundException>(
() => _orderAppService.GetAsync(999999));
}
}
Test Configuration
Place test-specific configuration in appsettings.Testing.json:
{
"ConnectionStrings": {
"Default": "Server=localhost;Database=TestDb;..."
},
"Redis": {
"Configuration": "localhost:6379"
}
}
Mocking Dependencies
Override BeforeAddApplication to substitute mock implementations:
public class OrderAppServiceTests : SilkyIntegratedTest<OrderTestModule>
{
protected override void BeforeAddApplication(IServiceCollection services)
{
// Replace the real payment service with a mock
services.AddSingleton<IPaymentService, MockPaymentService>();
}
}
Parallel Test Isolation
xUnit test classes run in parallel by default. Each SilkyIntegratedTest instance creates its own TestServiceScope, so Scoped services are isolated between test classes. Use [Collection] groups to prevent resource conflicts (e.g., shared database):
[Collection("Database")]
public class OrderRepositoryTests : SilkyIntegratedTest<OrderTestModule> { }
[Collection("Database")]
public class ProductRepositoryTests : SilkyIntegratedTest<ProductTestModule> { }
Warning
Do not commit TestResults/, bin/, or obj/ directories to source control.
