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="*" />
<!-- xUnit(またはお好みのテストフレームワーク) -->
<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>
重要:テストプロジェクトの File Watcher 修正
Akka.Hosting.TestKit は実際の IHost インスタンスを起動しますが、デフォルトでは設定再読み込みのためにファイルウォッチャーが有効になっています。多くのテストを実行すると、Linux 上のファイル記述子制限(inotify 監視制限)を枯渇させます。
これをテストプロジェクトに追加してください。これはすべてのテストが実行される前に動作します。
// TestEnvironmentInitializer.cs
using System.Runtime.CompilerServices;
namespace YourApp.Tests;
internal static class TestEnvironmentInitializer
{
[ModuleInitializer]
internal static void Initialize()
{
// テストホストでの設定ファイル監視を無効にする
// Linux でのファイル記述子枯渇(inotify 監視制限)を防ぐ
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
依存アクターの代役として TestProbe を ActorRegistry に登録します。ExpectMsgAsync<T>() を使用してメッセージが送信されたことを検証します。
パターン3:自動応答 TestProbe
テスト対象アクターが依存関係との通信に Ask を使用する場合、プローブにメッセージを転送し、かつ応答も行ってタイムアウトを防ぐ自動応答アクターを作成します。
パターン4:永続アクターのテスト
WithInMemoryJournal() と WithInMemorySnapshotStore() を使用します。PoisonPill でアクターを強制終了し、クエリを実行してジャーナルからの復元を強制することで復旧をテストします。
パターン5:本番構成の再利用
テストでは HOCON 構成を複製する代わりに、常に本番拡張メソッドを再利用してください。 これにより、テストが本番とまったく同じ構成を使用することが保証されます。
protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
builder
.AddDraftSerializer() // 本番と同じ
.AddOrderDomainActors(AkkaExecutionMode.LocalTest) // 同じだが、ローカルモード
.WithInMemoryJournal().WithInMemorySnapshotStore(); // テスト固有のオーバーライド
}
パターン6:ローカルでのクラスタシャーディング
AkkaExecutionMode.LocalTest と GenericChildPerEntityParent を使用して、実際のクラスタなしでシャーディング動作をテストします。同じ拡張メソッド、異なるモード。
パターン7:非同期操作のための AwaitAssertAsync
アクターが非同期操作を実行する場合は AwaitAssertAsync を使用します。アサーションが成功するかタイムアウトになるまで再試行し、flaky テストを防ぎます。
await AwaitAssertAsync(() =>
{
_fakeReadModelService.SyncCallCount.Should().BeGreaterOrEqualTo(1);
}, TimeSpan.FromSeconds(3));
パターン8:シナリオベースの統合テスト
複数のアクターと状態遷移を持つ完全なビジネスワークフローをエンドツーエンドでテストします。すべてのドメインアクターを登録し、各ステップで状態を検証します。
共通パターンのまとめ
| パターン | ユースケース |
|---|---|
| 基本アクターテスト | 注入されたサービスを持つ単一アクター |
| TestProbe | アクターが依存関係にメッセージを送信することを検証 |
| 自動応答 | テスト時の Ask タイムアウトを回避 |
| 永続アクター | イベントソーシングと復旧をテスト |
| クラスタシャーディング | ローカルでシャーディング動作をテスト |
| AwaitAssertAsync | アクター内の非同期操作を処理 |
| シナリオテスト | エンドツーエンドのビジネスワークフロー |
ベストプラクティス
- アクターごとに1つのテストクラス - テストを集中させる
- ConfigureServices/ConfigureAkka をオーバーライド - ベースクラスを作成しない
- モックではなくフェイクを使用 - よりシンプルで保守性が高い
- 一度に1つのアクターをテスト - 依存関係には TestProbe を使用
- 本番パターンに合わせる - 同じ拡張メソッド、異なる
AkkaExecutionMode - 非同期には AwaitAssertAsync を使用 - flaky テストを防ぐ
- 復旧をテスト - アクターを強制終了して再起動し、永続性を検証
- ワークフローのシナリオテスト - 完全なビジネスフローをエンドツーエンドでテスト
- テストを高速に保つ - インメモリ永続性、実際のデータベースは使わない
- 意味のある名前を使用 -
Scenario_FirstTimePurchase_SuccessfulPayment
デバッグのヒント
- デバッグログを有効にする - TestKit コンストラクタに
LogLevel.Debugを渡す - ITestOutputHelper を使用する - テスト出力でアクターシステムのログを確認
- TestProbe を検査する -
probe.Messagesをチェックして何が送信されたか確認 - アクター状態を問い合わせる - デバッグ用の状態問い合わせメッセージを追加
- AwaitAssertAsync をログとともに使用 - アサーションが失敗する理由を確認
- ActorRegistry を確認 - アクターが正しく登録されているか検証
// デバッグログ付きコンストラクタ
public OrderActorTests(ITestOutputHelper output)
: base(output: output, logLevel: LogLevel.Debug)
{
}