なぜアッカTestKitユニット・テストに合格させるのThread.sleep追加するのでしょうか?

hotmeatballsoup:

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ので、何が私が間違ってここにやっていると修正は何ですか?前もって感謝します!

ジョンStanislavciuc:

@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送信者とキャプチャからのすべての着信メッセージ/応答をエミュレートしますTestActorexpectMsg(new SomeMessage(15))代わりにアサートの使用されています。これは、アサーションが行われる前に、メッセージを待つを受信すること内側遮断機構を有しています。これは、中に何が起こるかである俳優の一例をテストします

作るためにexpectMsg正しくアサート、あなたはオーバーライドする必要がありますequalsあなたのクラスのメソッドをSomeMessage

編集:

なぜアッカはSomeMessageの内部状態を変更する時に顔をしかめていますか?

アッカの大国の一つは、同期や待ち時間を必要としない/共有データへのアクセスを制御するために知らせることです。しかし、これはメッセージの不変性を達成することができます。あなたは俳優がそれを処理し、正確な時刻に変更することを変更可能なメッセージを送って想像してみてください。これは、競合状態を引き起こす可能性があります。読む本の詳細については。

そして、(2)同じことが役者の内部状態を変更するには適用されませんか?それはActorRefsが変更、または同様にその時にコミュニティのしかめ面をしてすることができます特性を有するようにするための「OK」である(もしそうなら、なぜ!)?

いいえ、これはここでは適用されません。いずれかの状態が俳優の中incapsulatedされた場合にのみ、それはそれを変更することができ、それは完全に罰金可変性を持っています。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=206569&siteId=1