代码之家  ›  专栏  ›  技术社区  ›  hotmeatballsoup

为什么添加thread.sleep会使我的AKKA测试套件单元测试通过?

  •  3
  • hotmeatballsoup  · 技术社区  · 6 年前

    爪哇8和阿卡 应用程序接口 )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 ,那么我在这里做错了什么?解决方法是什么?事先谢谢!

    2 回复  |  直到 6 年前
        1
  •  2
  •   Ivan Stanislavciuc    6 年前

    @阿西尔·阿兰巴里说的是对的,你不让你的演员完成它的工作。

    参与者具有异步特性,尽管他们没有实现 Runnable 它们与用于发送消息的线程分开执行。

    您向参与者发送消息,然后立即断言消息已更改。由于actor在异步上下文(即不同的线程)中运行,因此它仍然没有处理传入的消息。从而提出 Threed.sleep 允许参与者处理消息,并且只有在此之后断言才完成。

    我建议你对最初的设计做些改动,这样可以很好地与阿卡自然相结合。

    首先,Akka不建议使用具有可变性的消息。它们必须是不变的。在你的情况下,这是打破方法 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() . 这是AKKA的适当用法。

    这允许测试使用 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));
    }
    

    测试探针 模拟发件人并捕获来自的所有传入邮件/答复 测试者 . 注意 expectMsg(new SomeMessage(15)) 而不是断言。它有一个内部阻塞机制,在断言完成之前等待消息被接收。这就是发生在 testing actors example .

    使 expectMsg 断言正确,必须重写 equals 你班上的方法 体位

    编辑:

    为什么阿卡不同意改变某个消息的内部状态?

    Akka的一个功能是不需要同步或等待/通知来控制对共享数据的访问。但这只能通过消息不可变来实现。假设您发送了一个可变的消息,您在actor处理它的准确时间更改了它。这会导致比赛条件。读 this 了解更多详细信息。

    (2)这同样适用于改变演员的内部状态吗?对于actorrefs来说,拥有可以改变的属性是“可以”的吗,或者社区也不同意这一点(如果是,为什么!)?

    不,这里不适用。如果任何状态被包含在一个参与者中,并且 只有它能改变它 具有可变性是完全可以的。

        2
  •  1
  •   aran    6 年前

    我认为你不让那个演员做他的工作。也许 AkkaActor 开始他自己的线索?我想 Actor 确实实现了runnable,但我不是AKKA的专家。--&编辑演员是一个界面,很高兴我说我不是专家。

    我的猜测是,通过睡眠你的主线,你给演员的“线”时间完成他的方法。

    我知道这可能没什么帮助,但发表评论的时间太长了。:(