Vladimir Luzhin
Vladimir Luzhin

Reputation: 3

Some problems with testing Akka.net

I have an actor of the following kind:

public class BranchUsersActor : ReceiveActor
{
    public BranchUsersActor()
    {
        Receive<UserBeingOnline>((online) =>
        {
            var userActorName = $"user_{online.UserId}";
            if (Context.Child(userActorName).Equals(ActorRefs.Nobody))
            {                
                var user = Context.ActorOf(UserActor.Props(online.UserId, online.BranchId), userActorName);
                user.Tell(online);                    
            }
        });
    }

    public static Props Props(LoggingTags tags)
    {
        return Akka.Actor.Props.Create(() => new BranchUsersActor(tags));
    }
}

When testing this actor, I expect that I will have a child actor. I'm writing the next test to check this situation (using the NUnit framework):

[Test]
public void Test()
{
    var branchUserActor = Sys.ActorOf(BranchUsersActor.Props());
    branchUserActor.Tell(UserBeingOnline.Create(userId, branchId));

    var expectedChildActor = Sys.ActorSelection($"{actorPath}/user_{userId.AkkaPrepare()}")
        .ResolveOne(TimeSpan.FromSeconds(1)).Result;

    Assert.IsNotNull(expectedChildActor);
}

I expect that within a second I will receive the child actor on the specified path, but I get ActorNotFoundExpection.

If I'm doing something like this:

[Test]
public void Test()
{
    var branchUserActor = Sys.ActorOf(BranchUsersActor.Props());
    branchUserActor.Tell(UserBeingOnline.Create(userId, branchId));

    Task.Delay(100).ContinueWith(_ => 
    {
        var expectedChildActor = Sys.ActorSelection($"{actorPath}/user_{userId.AkkaPrepare()}")`enter code here`
            .ResolveOne(TimeSpan.FromSeconds(1)).Result;
    }
    Assert.IsNotNull(expectedChildActor);    
}

This works fine, but 1 of 10 times the test falls, because I get an ActorNotFoundException. But I wonder why the first option does not work the way I expect?

Am I doing something wrong?

Thanks in advance for the answer.

Upvotes: 0

Views: 215

Answers (1)

Bartosz Sypytkowski
Bartosz Sypytkowski

Reputation: 7542

branchUserActor.Tell(UserBeingOnline.Create(userId, branchId));

var expectedChildActor = Sys.ActorSelection($"{actorPath}/user_{userId.AkkaPrepare()}")
  .ResolveOne(TimeSpan.FromSeconds(1)).Result;

The problem here is that when you're telling UserBeingOnline, you're triggering an asynchronous action - a message has been send to branchUserActor, but it may not have been processed right away. In the meantime you're calling resolve one, which tells actor system to find a child of branchUserActor - a child, which is not yet there, since the parent didn't handle the message yet.

You can use AwaitAssert(() => Assert.IsNotNull(ResolveChild())) method to work with that.

Upvotes: 1

Related Questions