最近我试图为akka演员编写一些单元测试来测试演员的消息流。我在测试中发现了一些奇怪的行为:
字段:
private TestActorRef<Actor> sut;
private ActorSystem system;
JavaTestKit AnotherActor;
JavaTestKit YetAnotherActor;
系统和演员是在@Before带注释的方法中创建的:
@Before
public void setup() throws ClassNotFoundException {
system = ActorSystem.apply();
AnotherActor = new JavaTestKit(system);
YetAnotherActor = new JavaTestKit(system);
Props props = MyActor.props(someReference);
this.sut = system.of(props, "MyActor"); }
下一步
@Test
public void shouldDoSth() throws Exception {
// given actor
MyActor actor = (MyActor) sut.underlyingActor();
// when
SomeMessage message = new SomeMessage(Collections.emptyList());
sut.tell(message, AnotherActor.getRef());
// then
YetAnotherActor.expectMsgClass(
FiniteDuration.apply(1, TimeUnit.SECONDS),
YetSomeMessage.class);
}
在我的代码中,我有:
private void processMessage(SomeMessage message) {
final List<Entity> entities = message.getEntities();
if(entities.isEmpty()) {
YetAnotherActor.tell(new YetSomeMessage(), getSelf());
// return;
}
if (entities > workers.size()) {
throw new IllegalStateException("too many tasks to be started !");
}
}
基本上,有时(非常罕见)此类测试失败(在另一个操作系统上),并且抛出了processMessage方法的异常(由于业务逻辑导致IllegalStateException)。
主要是测试传递,因为YetAomeMessage消息被YetAnotherActor接收,尽管IllegateStateException错误也被抛出并记录在堆栈跟踪中。
正如我从akka TestActorRef文档中假设的那样:
这个特殊的ActorRef专门用于单线程环境中的单元测试。因此,它 将调度程序覆盖到CallingThreadDispatcher并将receiveTimeout设置为None。除此以外, 它的行为就像一个普通的ActorRef。您可以检索对底层actor的引用以测试内部逻辑。
我的系统只使用单线程来处理actor收到的消息。有人可以解释我为什么尽管有适当的断言,测试失败了吗?
当然,在使用正确的代码发送YetSomeMessage之后返回将会完成但我不明白另一个线程处理如何导致测试faiulre。
答案 0 :(得分:2)
由于您使用的是TestActorRef,因此您基本上在进行同步测试。作为一般经验法则,除非确实需要,否则不要使用TestActorRef。那个东西使用CallingThreadDispatcher,即它会窃取调用者线程来执行actor。因此,您的谜团的解决方案是,actor与测试运行在同一个线程上,因此异常最终会出现在测试线程上。
幸运的是,你的这个测试用例根本不需要TestActorRef。您可以将actor设置为普通的actor,并且一切都应该工作(即actor应该在一个适当的单独线程上)。请尝试使用异步测试支持http://doc.akka.io/docs/akka/2.4.0/scala/testing.html#Asynchronous_Integration_Testing_with_TestKit
执行所有操作