Javaの8とアッカ(のJava API)2.12:2.5.16ここに。私は、次のメッセージがあります。
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;
}
}
そして、次の俳優:
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();
}
}
そして、次のユニットテスト:
@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());
}
}
だから、すべて私がしていることを確認しますしようとしているTestActor
実際には受けずSomeMessage
、それはその内部状態を変化させることを。
私はこのユニットテストを実行すると、それが失敗し、俳優がメッセージを受信したことがないかのようです:
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'.
しかし、私は試験方法を変更してご紹介したときに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());
}
何が起きてる?!もちろん、私は私の俳優テストが散らばったくないsleeps
ので、何が私が間違ってここにやっていると修正は何ですか?前もって感謝します!
@Asier Aranbarriはあなたの俳優がその作業を完了するために聞かせていないと言って正しいです。
アクターは、非同期的な性質を持っており、彼らは実装していませんがRunnable
、彼らからのメッセージを送信するために使用されたスレッドとは別に実行されます。
あなたはアクターにメッセージを送信し、その後すぐにメッセージが変更されたと主張しています。非同期の文脈、すなわち、異なるスレッドで俳優が実行されると、それはまだ、着信メッセージを処理していません。このように置くThreed.sleep
俳優のプロセスにメッセージを可能にし、この主張が行われた後にのみ。
私はアッカ自然とよく結婚するあなたの最初のデザインにいくつかの変更を提案することができます。
まず第一に、アッカは、可変性のメッセージを使用することをお勧めしません。彼らは不変でなければなりません。あなたは、このケースでは、この方法で破壊されますSomeMessage#setAnotherNum
。それを除く:
public class SomeMessage {
private int anotherNum;
public SomeMessage(int anotherNum) {
this.anotherNum = anotherNum;
}
public int getAnotherNum() {
return anotherNum;
}
}
この後の新しいインスタンスを作成するSomeMessage
代わりに、中にあなたの受信メッセージを変更TestActor
し、バックにそれを送りますcontext.sender()
。同様に、ここで定義されました
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();
}
}
さて、代わりにメッセージの内部状態を変更する、新しいメッセージが新しい状態で作成され、以降のメッセージはに戻されますsender()
。これは、アッカの適切な使用方法です。
これはテストを使用することができます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
送信者とキャプチャからのすべての着信メッセージ/応答をエミュレートしますTestActor
。注expectMsg(new SomeMessage(15))
代わりにアサートの使用されています。これは、アサーションが行われる前に、メッセージを待つを受信すること内側遮断機構を有しています。これは、中に何が起こるかである俳優の一例をテストします。
作るためにexpectMsg
正しくアサート、あなたはオーバーライドする必要がありますequals
あなたのクラスのメソッドをSomeMessage
編集:
なぜアッカはSomeMessageの内部状態を変更する時に顔をしかめていますか?
アッカの大国の一つは、同期や待ち時間を必要としない/共有データへのアクセスを制御するために知らせることです。しかし、これはメッセージの不変性を達成することができます。あなたは俳優がそれを処理し、正確な時刻に変更することを変更可能なメッセージを送って想像してみてください。これは、競合状態を引き起こす可能性があります。読む本の詳細については。
そして、(2)同じことが役者の内部状態を変更するには適用されませんか?それはActorRefsが変更、または同様にその時にコミュニティのしかめ面をしてすることができます特性を有するようにするための「OK」である(もしそうなら、なぜ!)?
いいえ、これはここでは適用されません。いずれかの状態が俳優の中incapsulatedされた場合にのみ、それはそれを変更することができ、それは完全に罰金可変性を持っています。