Files
scadalink-design/AkkaDotNet/17-HostingTestKit.md
Joseph Doherty de636b908b Add Akka.NET reference documentation
Notes and documentation covering actors, remoting, clustering, persistence,
streams, serialization, hosting, testing, and best practices for the Akka.NET
framework used throughout the ScadaLink system.
2026-03-16 09:08:17 -04:00

6.3 KiB

17 — Akka.Hosting.TestKit

Overview

Akka.Hosting.TestKit extends the standard TestKit with full Microsoft.Extensions.Hosting integration. It spins up a complete host environment with DI, logging, configuration, and Akka.NET — making it ideal for integration tests that need the full application wiring.

In the SCADA system, Hosting.TestKit is used to test the full actor startup pipeline: DI-injected actors, protocol adapter factories, configuration loading, and the Device Manager singleton lifecycle — all within a controlled test environment.

When to Use

  • Integration tests that require DI (protocol adapter factories, configuration services, loggers)
  • Testing the full Akka.Hosting configuration pipeline (Remoting, Cluster, Singleton registration)
  • Verifying that the ActorRegistry correctly resolves actor references
  • Testing actors that depend on IServiceProvider or IOptions<T>

When Not to Use

  • Simple actor unit tests that don't need DI — use Akka.TestKit directly (see 16-TestKit.md)
  • Multi-node cluster tests — use MultiNodeTestRunner (see 18-MultiNodeTestRunner.md)
  • Performance tests — the hosting overhead adds latency that distorts benchmarks

Design Decisions for the SCADA System

Test Fixture Structure

Each integration test class inherits from Akka.Hosting.TestKit.TestKit and overrides configuration methods:

public class DeviceManagerIntegrationTests : Akka.Hosting.TestKit.TestKit
{
    public DeviceManagerIntegrationTests(ITestOutputHelper output)
        : base(output: output) { }

    protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
    {
        // Register test doubles for protocol adapters
        services.AddSingleton<IProtocolAdapterFactory, MockProtocolAdapterFactory>();
        services.AddSingleton<IOptions<SiteConfiguration>>(
            Options.Create(TestSiteConfig.TwoDevices()));
    }

    protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
    {
        builder
            .WithActors((system, registry, resolver) =>
            {
                var manager = system.ActorOf(
                    resolver.Props<DeviceManagerActor>(), "device-manager");
                registry.Register<DeviceManagerActor>(manager);
            });
    }

    [Fact]
    public async Task DeviceManager_should_create_device_actors_from_config()
    {
        var manager = await ActorRegistry.GetAsync<DeviceManagerActor>();
        manager.Tell(new GetManagedDevices());

        var devices = ExpectMsg<ManagedDeviceList>();
        Assert.Equal(2, devices.Devices.Count);
    }
}

Mock Protocol Adapters

Register mock protocol adapters that simulate equipment behavior without requiring actual network connections:

public class MockProtocolAdapterFactory : IProtocolAdapterFactory
{
    public Props CreateAdapterProps(DeviceConfig config, IDependencyResolver resolver)
    {
        return Props.Create(() => new MockDeviceActor(config));
    }
}

Testing Configuration Loading

Verify that site-specific configuration is correctly loaded and applied:

protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
    // Load test configuration
    var config = new ConfigurationBuilder()
        .AddJsonFile("test-appsettings.json")
        .Build();

    services.Configure<SiteConfiguration>(config.GetSection("ScadaSite"));
}

Common Patterns

ActorRegistry Assertions

Use ActorRegistry.GetAsync<T>() to verify actors were correctly registered during startup:

[Fact]
public async Task Should_register_all_required_actors()
{
    var manager = await ActorRegistry.GetAsync<DeviceManagerActor>();
    Assert.NotNull(manager);

    var health = await ActorRegistry.GetAsync<ClusterHealthActor>();
    Assert.NotNull(health);
}

Testing with TestProbe

TestProbe works alongside Hosting.TestKit:

[Fact]
public async Task DeviceManager_should_forward_commands()
{
    var probe = CreateTestProbe();
    var manager = await ActorRegistry.GetAsync<DeviceManagerActor>();

    // Tell the manager to use the probe for a specific device
    manager.Tell(new RegisterTestDevice("test-device", probe));
    manager.Tell(new SendCommand("cmd-1", "test-device", "Start", true));

    probe.ExpectMsg<SendCommand>(cmd => cmd.CommandId == "cmd-1");
}

Lifecycle Testing

Verify that the actor system starts and stops cleanly:

[Fact]
public async Task Should_shutdown_gracefully()
{
    var manager = await ActorRegistry.GetAsync<DeviceManagerActor>();
    Watch(manager);

    // Trigger coordinated shutdown
    await CoordinatedShutdown.Get(Sys).Run(CoordinatedShutdown.ClrExitReason.Instance);

    ExpectTerminated(manager);
}

Anti-Patterns

Duplicating Production Configuration

Test configuration should be minimal and focused on the test scenario. Do not copy the entire production Akka.Hosting setup into tests — only configure what the test needs. This prevents tests from breaking when production config changes.

Testing Cluster Behavior in Hosting.TestKit

Hosting.TestKit runs a single-node ActorSystem. Do not test cluster membership, failover, or Singleton migration here — those require MultiNodeTestRunner. Hosting.TestKit is for testing application-level actor behavior with DI, not distributed system behavior.

Slow Tests from Full Hosting Stack

Each Hosting.TestKit test spins up a full host. If you have hundreds of tests, this adds up. Reserve Hosting.TestKit for integration tests that genuinely need DI; use plain TestKit for unit tests.

Configuration Guidance

NuGet Package

Akka.Hosting.TestKit

This pulls in Akka.Hosting, Akka.TestKit.Xunit2, and Microsoft.Extensions.Hosting.Testing.

Test Project Structure

ScadaSystem.Tests/
  Unit/
    DeviceActorTests.cs          (plain TestKit)
    CommandHandlerTests.cs       (plain TestKit)
  Integration/
    DeviceManagerIntegTests.cs   (Hosting.TestKit)
    ConfigurationIntegTests.cs   (Hosting.TestKit)
  Fixtures/
    TestSiteConfig.cs
    MockProtocolAdapterFactory.cs

References