akka-net-testing-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAkka.NET Testing Patterns
Akka.NET 测试模式
When to Use This Skill
何时使用该技能
Use this skill when:
- Writing unit tests for Akka.NET actors
- Testing persistent actors with event sourcing
- Verifying actor interactions and message flows
- Testing actor supervision and lifecycle
- Mocking external dependencies in actor tests
- Testing cluster sharding behavior locally
- Verifying actor state recovery and persistence
在以下场景中使用该技能:
- 为Akka.NET actors编写单元测试
- 测试基于事件溯源的持久化actors
- 验证actor交互与消息流转
- 测试actor监督机制与生命周期
- 在actor测试中模拟外部依赖
- 本地测试集群分片行为
- 验证actor状态恢复与持久化能力
Reference Files
参考文件
- examples.md: Complete code samples for all testing patterns (Patterns 1-8 plus Reminders)
- anti-patterns-and-reference.md: Anti-patterns, traditional TestKit, CI/CD integration
- examples.md: 所有测试模式的完整代码示例(模式1-8及注意事项)
- anti-patterns-and-reference.md: 反模式、传统TestKit用法、CI/CD集成指南
Choosing Your Testing Approach
选择合适的测试方法
Use Akka.Hosting.TestKit (Recommended for 95% of Use Cases)
使用Akka.Hosting.TestKit(95%场景推荐)
When:
- Building modern .NET applications with
Microsoft.Extensions.DependencyInjection - Using Akka.Hosting for actor configuration in production
- Need to inject services into actors (,
IOptions,DbContext, HTTP clients, etc.)ILogger - Testing applications that use ASP.NET Core, Worker Services, or .NET Aspire
- Working with modern Akka.NET projects (Akka.NET v1.5+)
Advantages:
- Native dependency injection support - override services with fakes in tests
- Configuration parity with production (same extension methods work in tests)
- Clean separation between actor logic and infrastructure
- Type-safe actor registry for retrieving actors
适用场景:
- 基于构建现代化.NET应用
Microsoft.Extensions.DependencyInjection - 生产环境中使用Akka.Hosting进行actor配置
- 需要向actors注入服务(如、
IOptions、DbContext、HTTP客户端等)ILogger - 测试使用ASP.NET Core、Worker Services或.NET Aspire的应用
- 基于Akka.NET v1.5+的现代化项目
优势:
- 原生支持依赖注入 - 可在测试中用伪造服务替代真实服务
- 与生产环境配置一致(相同的扩展方法可在测试中使用)
- 清晰分离actor逻辑与基础设施
- 类型安全的actor注册表用于获取actor引用
Use Traditional Akka.TestKit
使用传统Akka.TestKit
When:
- Contributing to Akka.NET core library development
- Working in environments without (console apps, legacy systems)
Microsoft.Extensions - Legacy codebases using manual creation without DI
Props
See anti-patterns-and-reference.md for traditional TestKit patterns.
适用场景:
- 为Akka.NET核心库开发做贡献
- 工作环境中未使用(如控制台应用、遗留系统)
Microsoft.Extensions - 遗留代码库中使用手动创建且未依赖DI
Props
传统TestKit模式请参考anti-patterns-and-reference.md。
Core Principles (Akka.Hosting.TestKit)
核心原则(Akka.Hosting.TestKit)
- Inherit from - This is a framework base class, not a user-defined one
Akka.Hosting.TestKit.TestKit - Override - Replace real services with fakes/mocks
ConfigureServices() - Override - Configure actors using the same extension methods as production
ConfigureAkka() - Use - Type-safe retrieval of actor references
ActorRegistry - Composition over Inheritance - Fake services as fields, not base classes
- No Custom Base Classes - Use method overrides, not inheritance hierarchies
- Test One Actor at a Time - Use TestProbes for dependencies
- Match Production Patterns - Same extension methods, different
AkkaExecutionMode
- 继承自- 这是一个框架基类,而非用户自定义类
Akka.Hosting.TestKit.TestKit - 重写- 用伪造/模拟服务替换真实服务
ConfigureServices() - 重写- 使用与生产环境相同的扩展方法配置actors
ConfigureAkka() - 使用- 类型安全地获取actor引用
ActorRegistry - 组合优于继承 - 将伪造服务作为字段,而非基类
- 不使用自定义基类 - 使用方法重写,而非继承层级
- 一次测试一个actor - 为依赖项使用TestProbes
- 匹配生产模式 - 使用相同的扩展方法,不同的
AkkaExecutionMode
Required NuGet Packages
必需的NuGet包
xml
<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>xml
<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>CRITICAL: File Watcher Fix for Test Projects
关键:测试项目的文件监视器修复
Akka.Hosting.TestKit spins up real instances, which by default enable file watchers for configuration reload. When running many tests, this exhausts file descriptor limits on Linux (inotify watch limit).
IHostAdd this to your test project - it runs before any tests execute:
csharp
// 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");
}
}Why this matters:
- runs automatically before any test code
[ModuleInitializer] - Sets the environment variable globally for all instances
IHost - Prevents cryptic errors when running 100+ tests
inotify - Also applies to Aspire integration tests that use
IHost
Akka.Hosting.TestKit会启动真实的实例,默认情况下会启用配置重载的文件监视器。当运行大量测试时,这会耗尽Linux系统的文件描述符限制(inotify监视限制)。
IHost在测试项目中添加以下代码 - 它会在所有测试执行前运行:
csharp
// 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] - 为所有实例全局设置环境变量
IHost - 运行100+测试时避免出现模糊的错误
inotify - 同样适用于使用的Aspire集成测试
IHost
Testing Patterns Overview
测试模式概述
Each pattern below has a condensed description. See examples.md for complete code samples.
以下每个模式都有简要说明,完整代码示例请参考examples.md。
Pattern 1: Basic Actor Test
模式1:基础Actor测试
The foundation pattern. Override to inject fakes, override to register actors with the same extension methods as production.
ConfigureServices()ConfigureAkka()csharp
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);
}
}基础测试模式。重写注入伪造服务,重写使用与生产环境相同的扩展方法注册actors。
ConfigureServices()ConfigureAkka()csharp
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);
}
}Pattern 2: TestProbe for Actor Interactions
模式2:使用TestProbe验证Actor交互
Register a in the as a stand-in for a dependency actor. Use to verify messages were sent.
TestProbeActorRegistryExpectMsgAsync<T>()在中注册作为依赖actor的替代品。使用验证消息是否已发送。
ActorRegistryTestProbeExpectMsgAsync<T>()Pattern 3: Auto-Responding TestProbe
模式3:自动响应的TestProbe
When the actor under test uses to communicate with dependencies, create an auto-responder actor that forwards messages to a probe AND replies to avoid timeouts.
Ask当被测actor使用与依赖项通信时,创建一个自动响应actor,将消息转发给probe并回复,避免超时。
AskPattern 4: Testing Persistent Actors
模式4:测试持久化Actor
Use and . Test recovery by killing the actor with and querying to force recovery from journal.
WithInMemoryJournal()WithInMemorySnapshotStore()PoisonPill使用和。通过发送杀死actor并查询状态,验证其从日志中恢复的能力。
WithInMemoryJournal()WithInMemorySnapshotStore()PoisonPillPattern 5: Reuse Production Configuration
模式5:复用生产环境配置
Always reuse production extension methods in tests instead of duplicating HOCON config. This ensures tests use the exact same configuration as production.
csharp
protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
builder
.AddDraftSerializer() // Same as production
.AddOrderDomainActors(AkkaExecutionMode.LocalTest) // Same, but local mode
.WithInMemoryJournal().WithInMemorySnapshotStore(); // Test-specific overrides
}始终在测试中复用生产环境的扩展方法,而非重复编写HOCON配置。这确保测试使用与生产环境完全相同的配置。
csharp
protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
builder
.AddDraftSerializer() // 与生产环境一致
.AddOrderDomainActors(AkkaExecutionMode.LocalTest) // 配置相同,模式为本地测试
.WithInMemoryJournal().WithInMemorySnapshotStore(); // 测试专属覆盖配置
}Pattern 6: Cluster Sharding Locally
模式6:本地测试集群分片
Use with to test sharding behavior without an actual cluster. Same extension methods, different mode.
AkkaExecutionMode.LocalTestGenericChildPerEntityParent结合与,无需真实集群即可测试分片行为。使用相同的扩展方法,仅模式不同。
AkkaExecutionMode.LocalTestGenericChildPerEntityParentPattern 7: AwaitAssertAsync for Async Operations
模式7:使用AwaitAssertAsync处理异步操作
Use when actors perform async operations. It retries assertions until they pass or timeout, preventing flaky tests.
AwaitAssertAsynccsharp
await AwaitAssertAsync(() =>
{
_fakeReadModelService.SyncCallCount.Should().BeGreaterOrEqualTo(1);
}, TimeSpan.FromSeconds(3));当actors执行异步操作时,使用。它会重试断言直到通过或超时,避免不稳定的测试。
AwaitAssertAsynccsharp
await AwaitAssertAsync(() =>
{
_fakeReadModelService.SyncCallCount.Should().BeGreaterOrEqualTo(1);
}, TimeSpan.FromSeconds(3));Pattern 8: Scenario-Based Integration Tests
模式8:基于场景的集成测试
Test complete business workflows end-to-end with multiple actors and state transitions. Register all domain actors, verify state at each step.
端到端测试完整业务工作流,涉及多个actors与状态转换。注册所有领域actors,验证每个步骤的状态。
Common Patterns Summary
常见模式汇总
| 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 |
| 模式 | 适用场景 |
|---|---|
| 基础Actor测试 | 带有注入服务的单个actor |
| TestProbe用法 | 验证actor向依赖项发送消息 |
| 自动响应器 | 测试时避免 |
| 持久化Actor测试 | 测试事件溯源与恢复能力 |
| 集群分片测试 | 本地测试分片行为 |
| AwaitAssertAsync | 处理actor中的异步操作 |
| 场景测试 | 端到端业务工作流测试 |
Best Practices
最佳实践
- One test class per actor - Keep tests focused
- Override ConfigureServices/ConfigureAkka - Don't create base classes
- Use fakes, not mocks - Simpler, more maintainable
- Test one actor at a time - Use TestProbes for dependencies
- Match production patterns - Same extension methods, different
AkkaExecutionMode - Use AwaitAssertAsync for async - Prevents flaky tests
- Test recovery - Kill and restart actors to verify persistence
- Scenario tests for workflows - Test complete business flows end-to-end
- Keep tests fast - In-memory persistence, no real databases
- Use meaningful names -
Scenario_FirstTimePurchase_SuccessfulPayment
- 每个actor对应一个测试类 - 保持测试聚焦
- 重写ConfigureServices/ConfigureAkka - 不要创建基类
- 使用伪造服务而非模拟对象 - 更简单、更易维护
- 一次测试一个actor - 为依赖项使用TestProbes
- 匹配生产环境模式 - 使用相同的扩展方法,不同的
AkkaExecutionMode - 异步操作使用AwaitAssertAsync - 避免不稳定测试
- 测试恢复能力 - 杀死并重启actor以验证持久化
- 工作流使用场景测试 - 端到端测试完整业务流程
- 保持测试快速 - 使用内存持久化,不连接真实数据库
- 使用有意义的命名 - 例如
Scenario_FirstTimePurchase_SuccessfulPayment
Debugging Tips
调试技巧
- Enable debug logging - Pass to TestKit constructor
LogLevel.Debug - Use ITestOutputHelper - See actor system logs in test output
- Inspect TestProbe - Check to see what was sent
probe.Messages - Query actor state - Add state query messages for debugging
- Use AwaitAssertAsync with logging - See why assertions fail
- Check ActorRegistry - Verify actors are registered correctly
csharp
// Constructor with debug logging
public OrderActorTests(ITestOutputHelper output)
: base(output: output, logLevel: LogLevel.Debug)
{
}- 启用调试日志 - 向TestKit构造函数传入
LogLevel.Debug - 使用ITestOutputHelper - 在测试输出中查看actor系统日志
- 检查TestProbe - 查看确认发送的消息
probe.Messages - 查询actor状态 - 添加状态查询消息用于调试
- 结合日志使用AwaitAssertAsync - 查看断言失败的原因
- 检查ActorRegistry - 验证actors是否正确注册
csharp
// 带调试日志的构造函数
public OrderActorTests(ITestOutputHelper output)
: base(output: output, logLevel: LogLevel.Debug)
{
}