[英]Why does adding Thread.sleep make my Akka TestKit unit tests pass?
Java 8 and Akka ( Java API ) 2.12:2.5.16 here. Java 8和Akka( Java API )2.12:2.5.16。 I have the following message:
我有以下消息:
public class SomeMessage {
private int anotherNum;
public SomeMessage(int anotherNum) {
this.anotherNum = anotherNum;
}
public int getAnotherNum() {
return anotherNum;
}
public void setAnotherNum(int anotherNum) {
this.anotherNum = anotherNum;
}
}
And the following actor: 以下演员:
public class TestActor extends AbstractActor {
private Integer number;
public TestActor(Integer number) {
this.number = number;
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(message -> {
if (message instanceof SomeMessage) {
SomeMessage someMessage = (SomeMessage) message;
System.out.println("someMessage contains = " + someMessage.getAnotherNum());
someMessage.setAnotherNum(number);
}
}).build();
}
}
And the following unit test: 以下单元测试:
@RunWith(MockitoJUnitRunner.class)
public class TestActorTest {
static ActorSystem actorSystem;
@BeforeClass
public static void setup() {
actorSystem = ActorSystem.create();
}
@AfterClass
public static void teardown() {
TestKit.shutdownActorSystem(actorSystem, Duration.create("10 seconds"), true);
actorSystem = null;
}
@Test
public void should_alter_some_message() {
// given
ActorRef testActor = actorSystem.actorOf(Props.create(TestActor.class, 10), "test.actor");
SomeMessage someMessage = new SomeMessage(5);
// when
testActor.tell(someMessage, ActorRef.noSender());
// then
assertEquals(10, someMessage.getAnotherNum());
}
}
So all I'm trying to verify is that TestActor
does in fact receive a SomeMessage
and that it alters its internal state. 所以我试图验证的是,
TestActor
确实接收了SomeMessage
并且它改变了它的内部状态。
When I run this unit test, it fails and is as if the actor never receives the message: 当我运行这个单元测试时,它失败了,好像演员从未收到过消息:
java.lang.AssertionError:
Expected :10
Actual :5
<Click to see difference>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
<rest of trace omitted for brevity>
[INFO] [01/30/2019 12:50:26.780] [default-akka.actor.default-dispatcher-2] [akka://default/user/test.actor] Message [myapp.actors.core.SomeMessage] without sender to Actor[akka://default/user/test.actor#2008219661] was not delivered. [1] dead letters encountered. If this is not an expected behavior, then [Actor[akka://default/user/test.actor#2008219661]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
But when I modify the test method and introduce a Thread.sleep(5000)
into it (after the tell(...)
) it passes with flying colors: 但是当我修改测试方法并将
Thread.sleep(5000)
引入其中时(在tell(...)
)它会以飞行颜色传递:
@Test
public void should_alter_some_message() throws InterruptedException {
// given
ActorRef testActor = actorSystem.actorOf(Props.create(TestActor.class, null, 10), "test.actor");
SomeMessage someMessage = new SomeMessage(5);
// when
testActor.tell(someMessage, ActorRef.noSender());
Thread.sleep(5000);
// then
assertEquals(10, someMessage.getAnotherNum());
}
What's going on here?! 这里发生了什么?! Obviously I don't want my actor tests littered with
sleeps
, so what am I doing wrong here and what is the fix? 显然我不希望我的演员测试充满了
sleeps
,所以我在这里做错了什么,修复是什么? Thanks in advance! 提前致谢!
@Asier Aranbarri is correct by saying that you don't let you actor to finish its work. @Asier Aranbarri说你不让演员完成它的工作是正确的。
Actors have asynchronous nature and though they don't implement Runnable
they are executed separately from the thread that is used to send a message from. Actor具有异步性质,虽然它们不实现
Runnable
,但它们与用于发送消息的线程分开执行。
You send a message to an actor and then immediately assert that the message was changed. 您向actor发送消息,然后立即断言消息已更改。 As actor runs in asynchronous context, ie different thread, it still has not processed the incoming message.
由于actor在异步上下文中运行,即不同的线程,它仍然没有处理传入的消息。 Thus putting
Threed.sleep
allows actor process the message and only after this the assertion is done. 因此,使用
Threed.sleep
允许actor处理消息,并且仅在此之后断言完成。
I may suggest some changes to your initial design that would marry well with akka nature. 我可能会建议您对初始设计进行一些更改,以便与akka性质结合得很好。
First of all, akka does not suggest using messages with mutability. 首先,akka不建议使用具有可变性的消息。 They must be immutable.
它们必须是不变的。 In you case this is broken with method
SomeMessage#setAnotherNum
. 在你的情况下,这是通过方法
SomeMessage#setAnotherNum
。 Remove it: 去掉它:
public class SomeMessage {
private int anotherNum;
public SomeMessage(int anotherNum) {
this.anotherNum = anotherNum;
}
public int getAnotherNum() {
return anotherNum;
}
}
After this create a new instance of SomeMessage
instead of changing your incoming message in TestActor
and send it back to context.sender()
. 在此之后创建
SomeMessage
的新实例,而不是在TestActor
中更改传入消息并将其发送回context.sender()
。 Like defined here 像这里定义的那样
static public class TestActor extends AbstractActor {
private Integer number;
public TestActor(Integer number) {
this.number = number;
}
@Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(message -> {
if (message instanceof SomeMessage) {
SomeMessage someMessage = (SomeMessage) message;
System.out.println("someMessage contains = " + someMessage.getAnotherNum());
context().sender().tell(new SomeMessage(number + someMessage.getAnotherNum()), context().self());
}
}).build();
}
}
Now, instead of changing inner state of a message, a new message is created with new state and the later message is returned back to sender()
. 现在,不是更改消息的内部状态,而是使用新状态创建新消息,然后将后面的消息返回给
sender()
。 This is the appropriate usage of akka. 这是akka的适当用法。
This allows test to use a TestProbe
and be redefined as follows 这允许测试使用
TestProbe
并重新定义如下
@Test
public void should_alter_some_message() {
// given
ActorRef testActor = actorSystem.actorOf(Props.create(TestActor.class,10));
TestJavaActor.SomeMessage someMessage = new SomeMessage(5);
TestProbe testProbe = TestProbe.apply(actorSystem);
// when
testActor.tell(someMessage, testProbe.ref());
// then
testProbe.expectMsg(new SomeMessage(15));
}
TestProbe
emulates a sender and captures all incoming message/replies from TestActor
. TestProbe
模拟发件人并从TestActor
捕获所有传入的消息/回复。 Note that expectMsg(new SomeMessage(15))
is used instead of an assert. 请注意,使用
expectMsg(new SomeMessage(15))
而不是断言。 It has an inner blocking mechanism that waits for message to be received before the assertion is done. 它有一个内部阻塞机制,在断言完成之前等待接收消息。 This is what happens in testing actors example .
这是测试演员示例中发生的事情。
To make expectMsg
assert correctly, you must override equals
method in your class SomeMessage
要使
expectMsg
正确断言,必须在类SomeMessage
重写equals
方法
Edit: 编辑:
Why does Akka frown upon changing SomeMessage's internal state?
为什么Akka在改变SomeMessage的内部状态时皱眉?
One of the powers of akka is that it does not require synchronisation or wait/notify to control access to shared data. akka的一个强大功能是它不需要同步或wait / notify来控制对共享数据的访问。 But this can be achieved only with message immutability.
但这只能通过消息不变性来实现。 Imagine you send a mutable message that you change at the exact time actor processes it.
想象一下,您发送了一条可变消息,您可以在actor处理它的确切时间更改它。 This can cause race conditions.
这可能会导致竞争条件。 Read this for more details.
阅读本文了解更多详情。
And (2) does the same apply to changing Actors' internal state?
并且(2)同样适用于改变演员的内部状态? Is it "OK" for ActorRefs to have properties that can be changed, or does the community frown upon that as well (and if so, why!)?
对于ActorRefs来说,“OK”是否具有可以更改的属性,或者社区是否也对此感到不满(如果是,为什么!)?
No, this does not apply here. 不,这不适用于此。 If any state is incapsulated within an actor and only it can change it , it's completely fine having mutability.
如果任何一个状态被封装在一个actor中, 只有它可以改变它 ,那么它具有可变性就完全没问题了。
I think you don't let the actor do his job. 我想你不要让演员做他的工作。 Maybe the
AkkaActor
starts his own thread? 也许
AkkaActor
开始自己的线程? I think Actor
does implement Runnable, but I'm not really an expert on Akka. 我认为
Actor
确实实现了Runnable,但我并不是Akka的专家。 --> edit Actor is an interface, glad I said I was not an expert.. - >编辑Actor是一个界面,很高兴我说我不是专家..
My guess is that by sleeping your main thread, you give time to the Actor's "thread" to finish his method. 我的猜测是,通过睡眠主线程,你可以给Actor的“线程”留出时间来完成他的方法。
I know this may not be helpful, but was way too long to put it in comment. 我知道这可能没有帮助,但是太长时间没有评论。 : (
:(
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.