测试从测试参与者发送的消息时,akkaNet 测试套件出现问题
akkaNet test kit issue when testing messges sent from tested actor
我正在尝试测试从测试参与者发送的消息,但收到超时异常和死信信息。
因为我正在使用 ninject - 创建了一个模拟方法,它总是使用探测演员参考重播。
我在这里遗漏了什么吗?
Assert.Fail failed. Failed: Timeout 00:00:03 while waiting for a message of type System.Type
at Akka.TestKit.TestKitBase.InternalExpectMsgEnvelope(Nullable`1 timeout, Action`2 assert, String hint, Boolean shouldLog)
at Akka.TestKit.TestKitBase.InternalExpectMsgEnvelope(Nullable`1 timeout, Action`1 msgAssert, Action`1 senderAssert, String hint)
at Akka.TestKit.TestKitBase.InternalExpectMsg(Nullable`1 timeout, Action`1 msgAssert, String hint)
at Akka.TestKit.TestKitBase.ExpectMsg(T message, Nullable`1 timeout, String hint)
at
AutoApply.UnitTests.SomethingProcessorActors.SomethingProcessorActorTests.SomethingProcessorActorWhenMergeDataAndGetsNoProfilesLogsThat()
in SomethingProcessorActorTests.cs: line 58
[WARNING][12/02/2016 16:12:43][Thread 0009][akka://test/user/testProbe] DeadLetter from [akka://test/temp/d]
to [akka://test/user/testProbe]:
[INFO][12/02/2016 16:12:43][Thread 0011][akka://test/user/testProbe] Message
GetOneSomethingAndRemoveFromList from akka://test/temp/d to
akka://test/user/testProbe was not delivered. 1 dead letters
encountered.
Debug Trace:
Setting probe reference: akka://test/user/testProbe
GetDataActorPath for:SomethingsDataActor
GetDataActorPath =>akka://test/user/testProbe
GetDataActorPath for:SomethingCollectorActor
GetDataActorPath =>akka://test/user/testProbe
[TestClass]
public class SomethingProcessorActorTests : TestKit
{
/// <summary>The factory helper</summary>
private IMockingExtension factoryHelper;
private TestProbe probeActorRef;
/// <summary>Configurations this instance.</summary>
[TestInitialize]
public void Config()
{
this.probeActorRef = this.CreateTestProbe("testProbe");
this.factoryHelper = new MockingFactoryHelper();
this.factoryHelper.SetProbe(this.probeActorRef.TestActor);
}
/// <summary>Somethings the processor actor when merge data and gets no profiles logs that.</summary>
[TestMethod]
public void SomethingProcessorActorWhenMergeDataAndGetsNoProfilesLogsThat()
{
// arrange
var actor =
this.Sys.ActorOf(
Props.Create(() => new SomethingProcessorActor(this.factoryHelper as IActorPathAndFactory)),
"SomethingActor");
// act
actor.Tell(new SomethingProcessorActor.ProcessSomethings());
// assert
this.probeActorRef.ExpectMsgFrom<SomethingsDataActor.GetOneSomethingAndRemoveFromList>(actor, new TimeSpan(0, 0, 0, 5));
}
}
=======================
public partial class SomethingProcessorActor : ReceiveActor
{
/// <summary>The helper</summary>
private readonly IActorPathAndFactory helper;
/// <summary>The log</summary>
private readonly ILoggingAdapter log = Context.GetLogger();
/// <summary>The vote execution profile</summary>
private List<SomethingProcessingObject> voteExecutionProfile = new List<SomethingProcessingObject>();
/// <summary>
/// Initializes a new instance of the <see cref="SomethingProcessorActor"/> class.
/// </summary>
/// <param name="helper">
/// The helper.
/// </param>
public SomethingProcessorActor(IActorPathAndFactory helper)
{
this.helper = helper;
this.Receive<ProcessSomethings>(
x =>
{
this.log.Debug("Received: ProcessSomethings");
this.BecomeStacked(this.Working);
this.RetriveSomethingAndPushForProcessing();
});
}
/// <summary>Supervisors strategy.</summary>
/// <returns>Supervisors strategy for that actor</returns>
protected override SupervisorStrategy SupervisorStrategy()
{
return new AllForOneStrategy(10, 3000, Decider.From(x => Directive.Stop));
}
/// <summary>
/// The merge data.
/// </summary>
private void RetriveSomethingAndPushForProcessing()
{
this.log.Debug($"Processing Somethings...");
var SomethingActor1 = this.helper.GetActorPath(ActorsEnum.SomethingsDataActor);
var SomethingActor2 = this.helper.GetActorPath(ActorsEnum.SomethingCollectorActor);
var something = (SomethingDto)SomethingActor1.Ask(new SomethingsDataActor.GetOneSomethingAndRemoveFromList()).Result;
while (Something.SomethingId>0)
{
this.log.Debug($"Sending data to SomethingCollector with Something id: {Something.SomethingId}");
SomethingActor2.Tell(new SomethingCollectorActor.ProcessSomethingDto(Something));
Something = (SomethingDto)SomethingActor1.Ask(new SomethingsDataActor.GetOneSomethingAndRemoveFromList()).Result;
}
this.log.Debug("Sending data to SomethingCollector -- ALL SENT");
this.UnbecomeStacked();
}
The mock objects just send probe actor as per every request
public ActorSelection GetActorPath(ActorsEnum actorsEnum)
{
Debug.WriteLine("GetDataActorPath for:" + actorsEnum);
Debug.WriteLine("GetDataActorPath =>" + this.probeRef.Path);
return this.Sys.ActorSelection(this.probeRef.Path);
}
public void SetProbe(IActorRef actorRef)
{
Debug.WriteLine("Setting probe reference: " + actorRef.Path);
this.probeRef = actorRef;
}
好的,所以有几件事。
首先:您期待的消息类型为:SomethingsDataActor.GetOneSomethingAndRemoveFromList
。
但看起来您实际上并没有将此消息传递给由 testprobe 表示的 actorref。但是很难确定,因为你只粘贴了一半的代码。
第二个:
- 在 actor 内部使用 ask 被认为是一种反模式,可以通过采用更对话式的交流方式轻松避免。
- 使用actor.ask()。结果更糟,因为如果不小心,可能会导致死锁。 (当数据库 actor 因网络中断而崩溃时会发生什么?可能永远不会发回任何响应,并且默认的 Ask 超时是无限的)
Ask 应该真正用于与 actor 系统外部的 actor 交流。
问题出在继承 TestClass 的模拟 class,
决定 "Actor System Reference"
return this.Sys.ActorSelection(this.probeRef.Ref.Path);
但应该是:
return this.probeRef.ActorSelection(this.probeRef.Ref.Path);
这种继承正在创建第二个独立的演员系统.....
感谢@Dantar 的帮助!
我正在尝试测试从测试参与者发送的消息,但收到超时异常和死信信息。 因为我正在使用 ninject - 创建了一个模拟方法,它总是使用探测演员参考重播。 我在这里遗漏了什么吗?
Assert.Fail failed. Failed: Timeout 00:00:03 while waiting for a message of type System.Type at Akka.TestKit.TestKitBase.InternalExpectMsgEnvelope(Nullable`1 timeout, Action`2 assert, String hint, Boolean shouldLog) at Akka.TestKit.TestKitBase.InternalExpectMsgEnvelope(Nullable`1 timeout, Action`1 msgAssert, Action`1 senderAssert, String hint) at Akka.TestKit.TestKitBase.InternalExpectMsg(Nullable`1 timeout, Action`1 msgAssert, String hint) at Akka.TestKit.TestKitBase.ExpectMsg(T message, Nullable`1 timeout, String hint) at
AutoApply.UnitTests.SomethingProcessorActors.SomethingProcessorActorTests.SomethingProcessorActorWhenMergeDataAndGetsNoProfilesLogsThat() in SomethingProcessorActorTests.cs: line 58
[WARNING][12/02/2016 16:12:43][Thread 0009][akka://test/user/testProbe] DeadLetter from [akka://test/temp/d]
to [akka://test/user/testProbe]: [INFO][12/02/2016 16:12:43][Thread 0011][akka://test/user/testProbe] Message GetOneSomethingAndRemoveFromList from akka://test/temp/d to akka://test/user/testProbe was not delivered. 1 dead letters encountered. Debug Trace: Setting probe reference: akka://test/user/testProbe GetDataActorPath for:SomethingsDataActor GetDataActorPath =>akka://test/user/testProbe GetDataActorPath for:SomethingCollectorActor GetDataActorPath =>akka://test/user/testProbe
[TestClass]
public class SomethingProcessorActorTests : TestKit
{
/// <summary>The factory helper</summary>
private IMockingExtension factoryHelper;
private TestProbe probeActorRef;
/// <summary>Configurations this instance.</summary>
[TestInitialize]
public void Config()
{
this.probeActorRef = this.CreateTestProbe("testProbe");
this.factoryHelper = new MockingFactoryHelper();
this.factoryHelper.SetProbe(this.probeActorRef.TestActor);
}
/// <summary>Somethings the processor actor when merge data and gets no profiles logs that.</summary>
[TestMethod]
public void SomethingProcessorActorWhenMergeDataAndGetsNoProfilesLogsThat()
{
// arrange
var actor =
this.Sys.ActorOf(
Props.Create(() => new SomethingProcessorActor(this.factoryHelper as IActorPathAndFactory)),
"SomethingActor");
// act
actor.Tell(new SomethingProcessorActor.ProcessSomethings());
// assert
this.probeActorRef.ExpectMsgFrom<SomethingsDataActor.GetOneSomethingAndRemoveFromList>(actor, new TimeSpan(0, 0, 0, 5));
}
}
=======================
public partial class SomethingProcessorActor : ReceiveActor
{
/// <summary>The helper</summary>
private readonly IActorPathAndFactory helper;
/// <summary>The log</summary>
private readonly ILoggingAdapter log = Context.GetLogger();
/// <summary>The vote execution profile</summary>
private List<SomethingProcessingObject> voteExecutionProfile = new List<SomethingProcessingObject>();
/// <summary>
/// Initializes a new instance of the <see cref="SomethingProcessorActor"/> class.
/// </summary>
/// <param name="helper">
/// The helper.
/// </param>
public SomethingProcessorActor(IActorPathAndFactory helper)
{
this.helper = helper;
this.Receive<ProcessSomethings>(
x =>
{
this.log.Debug("Received: ProcessSomethings");
this.BecomeStacked(this.Working);
this.RetriveSomethingAndPushForProcessing();
});
}
/// <summary>Supervisors strategy.</summary>
/// <returns>Supervisors strategy for that actor</returns>
protected override SupervisorStrategy SupervisorStrategy()
{
return new AllForOneStrategy(10, 3000, Decider.From(x => Directive.Stop));
}
/// <summary>
/// The merge data.
/// </summary>
private void RetriveSomethingAndPushForProcessing()
{
this.log.Debug($"Processing Somethings...");
var SomethingActor1 = this.helper.GetActorPath(ActorsEnum.SomethingsDataActor);
var SomethingActor2 = this.helper.GetActorPath(ActorsEnum.SomethingCollectorActor);
var something = (SomethingDto)SomethingActor1.Ask(new SomethingsDataActor.GetOneSomethingAndRemoveFromList()).Result;
while (Something.SomethingId>0)
{
this.log.Debug($"Sending data to SomethingCollector with Something id: {Something.SomethingId}");
SomethingActor2.Tell(new SomethingCollectorActor.ProcessSomethingDto(Something));
Something = (SomethingDto)SomethingActor1.Ask(new SomethingsDataActor.GetOneSomethingAndRemoveFromList()).Result;
}
this.log.Debug("Sending data to SomethingCollector -- ALL SENT");
this.UnbecomeStacked();
}
The mock objects just send probe actor as per every request
public ActorSelection GetActorPath(ActorsEnum actorsEnum)
{
Debug.WriteLine("GetDataActorPath for:" + actorsEnum);
Debug.WriteLine("GetDataActorPath =>" + this.probeRef.Path);
return this.Sys.ActorSelection(this.probeRef.Path);
}
public void SetProbe(IActorRef actorRef)
{
Debug.WriteLine("Setting probe reference: " + actorRef.Path);
this.probeRef = actorRef;
}
好的,所以有几件事。
首先:您期待的消息类型为:SomethingsDataActor.GetOneSomethingAndRemoveFromList
。
但看起来您实际上并没有将此消息传递给由 testprobe 表示的 actorref。但是很难确定,因为你只粘贴了一半的代码。
第二个:
- 在 actor 内部使用 ask 被认为是一种反模式,可以通过采用更对话式的交流方式轻松避免。
- 使用actor.ask()。结果更糟,因为如果不小心,可能会导致死锁。 (当数据库 actor 因网络中断而崩溃时会发生什么?可能永远不会发回任何响应,并且默认的 Ask 超时是无限的)
Ask 应该真正用于与 actor 系统外部的 actor 交流。
问题出在继承 TestClass 的模拟 class,
决定 "Actor System Reference"
return this.Sys.ActorSelection(this.probeRef.Ref.Path);
但应该是:
return this.probeRef.ActorSelection(this.probeRef.Ref.Path);
这种继承正在创建第二个独立的演员系统.....
感谢@Dantar 的帮助!