mcpField
A
akka-net-testing-patterns

akka-net-testing-patterns

github.com/aarononthewebテスト・品質1.2k421 回インストール今日更新
リポジトリ

akka-net-testing-patterns について

Akka.NET を使う .NET チーム向けのテストガイドです。Actor、永続化、相互作用、ローカルシャーディングを扱い、多くの現代的なアプリでは Akka.Hosting.TestKit を推奨します。

主な機能

  1. 01偽の依存サービスを注入した Actor テスト
  2. 02TestProbe によるメッセージ検証
  3. 03永続化と復旧のテスト
  4. 04ローカルでのシャーディングテスト
  5. 05非同期アサーションとシナリオテスト

活用シーン

  1. 01Actor のメッセージフロー確認
  2. 02イベントソーシング Actor の復旧検証
  3. 03クラスターなしでのシャーディングテスト
  4. 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)

  1. Akka.Hosting.TestKit.TestKit を継承する - これはフレームワークの基底クラスであり、ユーザー定義のクラスではありません
  2. ConfigureServices() をオーバーライドする - 実際のサービスをフェイク/モックに置き換えます
  3. ConfigureAkka() をオーバーライドする - 本番環境と同じ拡張メソッドを使用してアクターを構成します
  4. ActorRegistry を使用する - 型安全な方法でアクター参照を取得します
  5. 継承よりコンポジションを優先する - フェイクサービスは基底クラスではなくフィールドとして使用します
  6. 独自の基底クラスを作成しない - 継承階層ではなくメソッドのオーバーライドを使用します
  7. 一度に 1 つのアクターをテストする - 依存関係には TestProbe を使用します
  8. 本番環境のパターンに合わせる - 同じ拡張メソッドを使用し、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. アクターごとにテストクラスを 1 つ作成する - テストの対象を絞ります
  2. ConfigureServices/ConfigureAkka をオーバーライドする - 基底クラスは作成しないでください
  3. モックではなくフェイクを使う - よりシンプルで、保守しやすくなります
  4. 一度に 1 つのアクターをテストする - 依存先には TestProbe を使用します
  5. 本番のパターンに合わせる - 同じ拡張メソッドを使用し、AkkaExecutionMode だけを切り替えます
  6. 非同期処理には AwaitAssertAsync を使用する - 不安定なテストを防止します
  7. 復旧をテストする - アクターを停止して再起動し、永続化を検証します
  8. ワークフローにはシナリオテストを使用する - ビジネスフロー全体をエンドツーエンドでテストします
  9. テストを高速に保つ - インメモリ永続化を使用し、実際のデータベースは使いません
  10. 意味のある名前を付ける - Scenario_FirstTimePurchase_SuccessfulPayment

デバッグのヒント

  1. デバッグログを有効にする - TestKit コンストラクターに LogLevel.Debug を渡します
  2. ITestOutputHelper を使用する - テスト出力でアクターシステムのログを確認できます
  3. TestProbe を調べる - probe.Messages を確認して、送信されたメッセージを確認します
  4. アクターの状態を照会する - デバッグ用に状態照会メッセージを追加します
  5. ログ出力を伴う AwaitAssertAsync を使用する - アサーションが失敗する理由を確認できます
  6. ActorRegistry を確認する - アクターが正しく登録されていることを確認します
// Constructor with debug logging
public OrderActorTests(ITestOutputHelper output)
    : base(output: output, logLevel: LogLevel.Debug)
{
}

コメント