akka-net-testing-patterns について
Akka.NET を使う .NET チーム向けのテストガイドです。Actor、永続化、相互作用、ローカルシャーディングを扱い、多くの現代的なアプリでは Akka.Hosting.TestKit を推奨します。
主な機能
- 01偽の依存サービスを注入した Actor テスト
- 02TestProbe によるメッセージ検証
- 03永続化と復旧のテスト
- 04ローカルでのシャーディングテスト
- 05非同期アサーションとシナリオテスト
活用シーン
- 01Actor のメッセージフロー確認
- 02イベントソーシング Actor の復旧検証
- 03クラスターなしでのシャーディングテスト
- 04Actor ワークフローのエンドツーエンド検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ aaronontheweb/ dotnet-skills --skill akka-net-testing-patternsインストール後、Claude Code で/akka-net-testing-patternsを入力して読み込まれたか確認します。
同梱ファイルanti-patterns-and-reference.mdexamples.md
詳細
Akka.NET のテストパターン
このスキルを使用する場面
次のような場合に、このスキルを使用します。
- Akka.NET アクターのユニットテストを作成する
- イベントソーシングを使用する永続アクターをテストする
- アクター間のやり取りやメッセージフローを検証する
- アクターの監視とライフサイクルをテストする
- アクターのテストで外部依存関係をモックする
- クラスターシャーディングの動作をローカルでテストする
- アクターの状態復旧と永続化を検証する
参照ファイル
- examples.md:すべてのテストパターンの完全なコードサンプル(パターン 1~8 とリマインダー)
- anti-patterns-and-reference.md:アンチパターン、従来の TestKit、CI/CD との統合
テスト方法の選択
Akka.Hosting.TestKit を使用する(ユースケースの 95% に推奨)
使用する場面:
Microsoft.Extensions.DependencyInjectionを使用する最新の .NET アプリケーションを構築する- 本番環境でアクターの構成に Akka.Hosting を使用する
- アクターにサービス(
IOptions、DbContext、ILogger、HTTP クライアントなど)を注入する必要がある - ASP.NET Core、Worker Services、または .NET Aspire を使用するアプリケーションをテストする
- 最新の Akka.NET プロジェクト(Akka.NET v1.5 以降)で作業する
利点:
- 標準で依存性注入をサポートし、テストでサービスをフェイクに置き換えられます
- 本番環境と構成が一致します(同じ拡張メソッドをテストでも使用できます)
- アクターのロジックとインフラストラクチャを明確に分離できます
- アクターを取得するための型安全なレジストリを使用できます
従来の Akka.TestKit を使用する
使用する場面:
- Akka.NET コアライブラリの開発に貢献する
Microsoft.Extensionsを使用できない環境(コンソールアプリ、レガシーシステム)で作業する- DI を使わず、手動で
Propsを作成するレガシーコードベースを扱う
従来の TestKit のパターンについては、anti-patterns-and-reference.md を参照してください。
基本原則(Akka.Hosting.TestKit)
Akka.Hosting.TestKit.TestKitを継承する - これはフレームワークの基底クラスであり、ユーザー定義のクラスではありませんConfigureServices()をオーバーライドする - 実際のサービスをフェイク/モックに置き換えますConfigureAkka()をオーバーライドする - 本番環境と同じ拡張メソッドを使用してアクターを構成しますActorRegistryを使用する - 型安全な方法でアクター参照を取得します- 継承よりコンポジションを優先する - フェイクサービスは基底クラスではなくフィールドとして使用します
- 独自の基底クラスを作成しない - 継承階層ではなくメソッドのオーバーライドを使用します
- 一度に 1 つのアクターをテストする - 依存関係には TestProbe を使用します
- 本番環境のパターンに合わせる - 同じ拡張メソッドを使用し、
AkkaExecutionModeを切り替えます
必須の NuGet パッケージ
<ItemGroup>
<PackageReference Include="Akka.Hosting.TestKit" Version="*" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
<PackageReference Include="FluentAssertions" Version="*" />
<PackageReference Include="Akka.Persistence.Hosting" Version="*" />
<PackageReference Include="Akka.Cluster.Hosting" Version="*" />
</ItemGroup>
重要:テストプロジェクトのファイルウォッチャー対策
Akka.Hosting.TestKit は実際の IHost インスタンスを起動します。これらのインスタンスでは、既定で構成の再読み込み用にファイルウォッチャーが有効になります。多数のテストを実行すると、Linux のファイルディスクリプター上限(inotify のウォッチ上限)を使い切ってしまいます。
テストの実行前に適用される以下の設定を、テストプロジェクトに追加してください。
// 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エラーが発生するのを防ぎます IHostを使用する Aspire 統合テストにも適用されます
テストパターンの概要
以下の各パターンでは、要点をまとめて説明しています。完全なコードサンプルについては、examples.md を参照してください。
パターン 1:基本的なアクターテスト
基礎となるパターンです。ConfigureServices() をオーバーライドしてフェイクを注入し、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);
}
}
パターン 2:アクター間のやり取りに TestProbe を使用する
依存アクターの代わりに、ActorRegistry に TestProbe を登録します。ExpectMsgAsync<T>() を使用して、メッセージが送信されたことを検証します。
パターン 3:自動応答する TestProbe
テスト対象のアクターが依存関係との通信に Ask を使用する場合は、メッセージをプローブに転送し、タイムアウトを防ぐために応答も返す自動応答アクターを作成します。
パターン 4:永続化アクターのテスト
WithInMemoryJournal() と WithInMemorySnapshotStore() を使用します。アクターを PoisonPill で停止し、クエリを送信してジャーナルからの復旧を強制することで、復旧をテストします。
パターン 5:本番構成の再利用
テストでは、HOCON 構成を重複して記述するのではなく、常に本番用の拡張メソッドを再利用してください。これにより、テストで本番とまったく同じ構成を使用できます。
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
}
パターン 6:ローカルでのクラスターシャーディング
実際のクラスターを使わずにシャーディングの動作をテストするには、GenericChildPerEntityParent とともに AkkaExecutionMode.LocalTest を使用します。同じ拡張メソッドを使い、モードだけを切り替えます。
パターン 7:非同期操作での AwaitAssertAsync
アクターが非同期操作を実行する場合は、AwaitAssertAsync を使用します。アサーションが成功するかタイムアウトするまで再試行するため、不安定なテストを防止できます。
await AwaitAssertAsync(() =>
{
_fakeReadModelService.SyncCallCount.Should().BeGreaterOrEqualTo(1);
}, TimeSpan.FromSeconds(3));
パターン 8:シナリオベースの統合テスト
複数のアクターと状態遷移を使って、ビジネスワークフロー全体をエンドツーエンドでテストします。すべてのドメインアクターを登録し、各ステップで状態を検証します。
よくあるパターンのまとめ
| パターン | 使用例 |
|---|---|
| 基本的なアクターテスト | サービスを注入した単一のアクター |
| TestProbe | アクターが依存先にメッセージを送信することの検証 |
| 自動応答 | テスト中の Ask タイムアウトの回避 |
| 永続化アクター | イベントソーシングと復旧のテスト |
| クラスターシャーディング | ローカルでのシャーディング動作のテスト |
| AwaitAssertAsync | アクター内の非同期操作への対応 |
| シナリオテスト | ビジネスワークフローのエンドツーエンドテスト |
ベストプラクティス
- アクターごとにテストクラスを 1 つ作成する - テストの対象を絞ります
- ConfigureServices/ConfigureAkka をオーバーライドする - 基底クラスは作成しないでください
- モックではなくフェイクを使う - よりシンプルで、保守しやすくなります
- 一度に 1 つのアクターをテストする - 依存先には TestProbe を使用します
- 本番のパターンに合わせる - 同じ拡張メソッドを使用し、
AkkaExecutionModeだけを切り替えます - 非同期処理には AwaitAssertAsync を使用する - 不安定なテストを防止します
- 復旧をテストする - アクターを停止して再起動し、永続化を検証します
- ワークフローにはシナリオテストを使用する - ビジネスフロー全体をエンドツーエンドでテストします
- テストを高速に保つ - インメモリ永続化を使用し、実際のデータベースは使いません
- 意味のある名前を付ける -
Scenario_FirstTimePurchase_SuccessfulPayment
デバッグのヒント
- デバッグログを有効にする - TestKit コンストラクターに
LogLevel.Debugを渡します - ITestOutputHelper を使用する - テスト出力でアクターシステムのログを確認できます
- TestProbe を調べる -
probe.Messagesを確認して、送信されたメッセージを確認します - アクターの状態を照会する - デバッグ用に状態照会メッセージを追加します
- ログ出力を伴う AwaitAssertAsync を使用する - アサーションが失敗する理由を確認できます
- ActorRegistry を確認する - アクターが正しく登録されていることを確認します
// Constructor with debug logging
public OrderActorTests(ITestOutputHelper output)
: base(output: output, logLevel: LogLevel.Debug)
{
}