Search code examples
xunit.netakka.netakka-clusterakka-testkit

C# Akka.Net Unit Tests which built-In Assertion is the correct to wait for a published message?


I need some support in order to uderstand how I could successfully wait for an expected response message that is sent from within an actor Receive block.

The following example is different to what I could find so far in the web, because I use the Akka.Cluster / Akka.Cluster.Tools to Publish Messages instead of sending a message directly back 'Sender.Tell()' to the sender.

Question: How can I fetch, subscribe etc. this published SomeResponse message in my Unit Test?

public class SomeRequest { }
public class SomeResponse { }
public class MyDbTestActor : ReceiveActor
{
    private readonly IActorRef _mediator;

    public MyDbTestActor()
    {
        // ...
        _mediator = DistributedPubSub.Get(Context.System).Mediator;
        // ...
        Receive<SomeRequest>(o =>
        {
            // ...
            var response = new SomeResponse();
            _mediator.Tell(new Publish(Global.MYTopic, response)); // <-- ExpectMsg timeout ...
            
            //Sender.Tell(response); // if uncommented ExpectMsg is succeed.
        });
    }
}
class Global
{
    public static string MYTopic =nameof(MYTopic);
}
public class SomeActorTests : TestKit
{
    [Fact]
    public void SomeTest()
    {
        var actor = Sys.ActorOf(Props.Create<MyDbTestActor>());

        actor.Tell(new SomeRequest());
        
        ExpectMsg<SomeResponse>();
    }
}

Solution

  • I would just have the TestActor subscribe to that topic from the DistributedPubSub mediator:

    public class SomeActorTests: TestKit
    {
        private IActorRef _mediator;
        private string path;
    
        // pass in an Akka.Cluster config that performs a self-join, etc
        public SomeActorTests(Config config, ITestOutputHelper output) : base(config, output:output)
        {
            _mediator = DistributedPubSub.Get(Sys).Mediator;
        }
    
        [Fact]
        public void SomeTest()
        {
    
            // arrange
            var actor = Sys.ActorOf(Props.Create<MyDbTestActor>());
            _mediator.Tell(new Subscribe(Global.MYTopic, TestActor));
            ExpectMsg<SubscribeAck>(); // subscription confirmed
    
            // act
            actor.Tell(new SomeRequest());
            
            // assert
            ExpectMsg<SomeResponse>();
        }
    }
    

    That will close the circuit using the Mediator to connect your actor and the TestActor together.