Loading...
Loading...
Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.
npx skill4agent add aaronontheweb/dotnet-skills akka-net-testing-patternsMicrosoft.Extensions.DependencyInjectionIOptionsDbContextILoggerMicrosoft.ExtensionsPropsAkka.Hosting.TestKit.TestKitConfigureServices()ConfigureAkka()ActorRegistryAkkaExecutionMode<ItemGroup>
<!-- Core testing framework -->
<PackageReference Include="Akka.Hosting.TestKit" Version="*" />
<!-- xUnit (or your preferred test framework) -->
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
<!-- Assertions (recommended) -->
<PackageReference Include="FluentAssertions" Version="*" />
<!-- In-memory persistence for testing -->
<PackageReference Include="Akka.Persistence.Hosting" Version="*" />
<!-- If testing cluster sharding -->
<PackageReference Include="Akka.Cluster.Hosting" Version="*" />
</ItemGroup>IHost// TestEnvironmentInitializer.cs
using System.Runtime.CompilerServices;
namespace YourApp.Tests;
internal static class TestEnvironmentInitializer
{
[ModuleInitializer]
internal static void Initialize()
{
// Disable config file watching in test hosts
// Prevents file descriptor exhaustion (inotify watch limit) on Linux
Environment.SetEnvironmentVariable("DOTNET_HOSTBUILDER__RELOADCONFIGONCHANGE", "false");
}
}[ModuleInitializer]IHostinotifyIHostConfigureServices()ConfigureAkka()public class OrderActorTests : TestKit
{
private readonly FakeOrderRepository _fakeRepository = new();
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services.AddSingleton<IOrderRepository>(_fakeRepository);
}
protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
builder.WithInMemoryJournal().WithInMemorySnapshotStore();
builder.WithActors((system, registry, resolver) =>
{
registry.Register<OrderActor>(system.ActorOf(resolver.Props<OrderActor>(), "order-actor"));
});
}
[Fact]
public async Task CreateOrder_Success_SavesToRepository()
{
var orderActor = ActorRegistry.Get<OrderActor>();
var response = await orderActor.Ask<OrderCommandResult>(
new CreateOrder("ORDER-123", "CUST-456", 99.99m), RemainingOrDefault);
response.Status.Should().Be(CommandStatus.Success);
_fakeRepository.SaveCallCount.Should().Be(1);
}
}TestProbeActorRegistryExpectMsgAsync<T>()AskWithInMemoryJournal()WithInMemorySnapshotStore()PoisonPillprotected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
builder
.AddDraftSerializer() // Same as production
.AddOrderDomainActors(AkkaExecutionMode.LocalTest) // Same, but local mode
.WithInMemoryJournal().WithInMemorySnapshotStore(); // Test-specific overrides
}AkkaExecutionMode.LocalTestGenericChildPerEntityParentAwaitAssertAsyncawait AwaitAssertAsync(() =>
{
_fakeReadModelService.SyncCallCount.Should().BeGreaterOrEqualTo(1);
}, TimeSpan.FromSeconds(3));| Pattern | Use Case |
|---|---|
| Basic Actor Test | Single actor with injected services |
| TestProbe | Verify actor sends messages to dependencies |
| Auto-Responder | Avoid |
| Persistent Actor | Test event sourcing and recovery |
| Cluster Sharding | Test sharding behavior locally |
| AwaitAssertAsync | Handle async operations in actors |
| Scenario Tests | End-to-end business workflows |
AkkaExecutionModeScenario_FirstTimePurchase_SuccessfulPaymentLogLevel.Debugprobe.Messages// Constructor with debug logging
public OrderActorTests(ITestOutputHelper output)
: base(output: output, logLevel: LogLevel.Debug)
{
}