# 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](./16-TestKit.md))
- Multi-node cluster tests — use MultiNodeTestRunner (see [18-MultiNodeTestRunner.md](./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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
[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:

```csharp
[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:

```csharp
[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

- NuGet: <https://www.nuget.org/packages/Akka.Hosting.TestKit>
- Petabridge Bootcamp: <https://petabridge.com/bootcamp/lessons/unit-1/akka-hosting-testkit/>
