Open anna-git opened 7 years ago
Oddly enough, I found a solution. Akka.net may not have liked the StubChildActor
to be instantiated manually. When I register it as a type, it changes everything, the test passes ! I added a "ask" before telling the actor, to be sure everything has been subscribed first. But it's the IOC registration that was wrong and made akka.net bug (null reference exception inside the framework).
Here's my solution:
https://github.com/annayafi/akka.testkit.ioc.issue/blob/master/Solution/UnitTest.cs
So basically instead of this kind of registration:
private StubChildActor _childstub;
[SetUp]
public void SetUpContainer()
{
var container = new ContainerBuilder();
_childstub = new StubChildActor(CreateTestProbe());
container.RegisterInstance(_childstub).As<ChildActor>();
var build = container.Build();
new AutoFacDependencyResolver(build, Sys);
}
I registered this way without manual instantiation..:
private IContainer build;
[SetUp]
public void SetUpContainer()
{
var container = new ContainerBuilder();
container.Register(_ => CreateTestProbe()).As<TestProbe>().InstancePerDependency();
container.RegisterType<StubChildActor>().AsSelf().As<ChildActor>().SingleInstance(); //for the test's sake, we need to get the same instance.
build = container.Build();
new AutoFacDependencyResolver(build, Sys);
}
That's really funny, but hey, it's working now !
Still, sometimes, depending on the cpu power of the machines, the tests are still running into this problem. The line Akka.Actor.ActorBase.AroundReceive(Receive receive, Object message)
has a receive to null. The tests are randomly failing on build server and teskit method ExpectMessage fails...
@annayafi which object is it that's throwing the NRE in that statement?
receive
is null actually, in the call stack it's the DefaultActorState.GetBehaviorState
which returns null
I ran into the same problem. Took a deep dive using the debugger and dotPeek, and arrived at the same findings as OP.
I've set up an example repo at https://github.com/stijnherreman/AkkaDotNetIssue3039
Just like OP, my real scenario is creating a subclass and injecting a TestProbe
, but for the sake of simplicity I've skipped that in the example.
Debug information is logged to the test output, you can review it in full by running the unit tests. The important part is this:
[ERROR][16/10/2019 15:46:54][Thread 0023][akka://test/user/$a/$a] Object reference not set to an instance of an object.
Cause: System.NullReferenceException: Object reference not set to an instance of an object.
at Akka.Actor.ActorBase.AroundReceive(Receive receive, Object message)
at Akka.Actor.ActorCell.ReceiveMessage(Object message)
at Akka.Actor.ActorCell.Invoke(Envelope envelope)
It appears that, when registering an instance instead of a type, the behaviour of the actor is lost at some point or is never correctly set to begin with. Here _state.GetCurrentBehavior()
returns null
:
https://github.com/akkadotnet/akka.net/blob/537f81878377286c604a12a36e946fbe0aebb3ab/src/core/Akka/Actor/ActorCell.DefaultMessages.cs#L177-L179
I stopped debugging at this point and then found this issue. Probably the Akka.NET devs will have an easier time than me to continue investigating this :)
I thought I found a nice elegant way to test child actors in Akka, by using DI, and registering a subclass of my child actor, which would forward all messages to a TestProbe. But there seems to be a bug with akka.testkit. Here's a project where you can see what happens exactly: https://github.com/annayafi/akka.testkit.ioc.issue/blob/master/ConsoleApp1/UnitTest.cs
It should be noted that when you use the same exact scenario with the real actor system instead of Sys instance from TestKit, it works as expected, the subclass takes over the messages.
The nullreference exception we get is: Cause: System.NullReferenceException: La référence d'objet n'est pas définie à une instance d'un objet. à Akka.Actor.ActorBase.AroundReceive(Receive receive, Object message) à Akka.Actor.ActorCell.ReceiveMessage(Object message) à Akka.Actor.ActorCell.Invoke(Envelope envelope)
and it comes from ActorCell.DefaultMessages:
state.GetCurrentBehavior is returning null. Receive is null in ActorState. This doesn't happen with the real ActorSystem.