3

我是akka的新手,我正在java上尝试akka。我想了解演员内部业务逻辑的单元测试。我阅读了文档,演员中孤立的业务逻辑的唯一例子是:

static class MyActor extends UntypedActor {
  public void onReceive(Object o) throws Exception {
    if (o.equals("say42")) {
      getSender().tell(42, getSelf());
    } else if (o instanceof Exception) {
      throw (Exception) o;
    }
  }
  public boolean testMe() { return true; }
}

@Test
public void demonstrateTestActorRef() {
  final Props props = Props.create(MyActor.class);
  final TestActorRef<MyActor> ref = TestActorRef.create(system, props, "testA");
  final MyActor actor = ref.underlyingActor();
  assertTrue(actor.testMe());
}

虽然这很简单,但这意味着我要测试的方法是公开的。但是,考虑到参与者应该只通过消息进行通信,我的理解是没有理由拥有公共方法,所以我将我的方法设为私有。如下例所示:

public class LogRowParser extends AbstractActor {
    private final Logger logger = LoggerFactory.getLogger(LogRowParser.class);

    public LogRowParser() {
        receive(ReceiveBuilder.
                        match(LogRow.class, lr -> {                                
                            ParsedLog log = parse(lr.rowText);
                            final ActorRef logWriter = getContext().actorOf(Props.create(LogWriter.class));
                            logWriter.tell(log, self());
                        }).
                        matchAny(o -> logger.info("Unknown message")).build()
        );
    }

    private ParsedLog parse(String rowText) {
        // Log parsing logic
    }
}

所以要测试方法parse我要么:

  1. 需要它使包私有
  2. 或者测试演员的公共接口,即下一个演员LogWriter从我的演员那里收到正确的解析消息LogRowParser

我的问题:

  1. 选项#1 有什么缺点吗?假设参与者仅通过消息进行通信,封装和干净的开放接口就不那么重要了吗?
  2. 如果我尝试使用选项#2,有没有办法在下游测试中捕获从参与者发送的消息(测试LogRowParser和捕获LogWriter)?我查看了各种示例,JavaTestKit但所有示例都捕获了返回给发件人的响应消息,并且没有一个显示如何拦截发送给新参与者的消息。
  3. 我还有其他选择吗?

谢谢!

UPD: 忘了提到我还考虑了以下选项:

  • 将逻辑从actors中完全移到辅助类中。akka 是常见的做法吗?
  • Powermock ......但如果可以重新设计,我会尽量避免它
4

2 回答 2

2

确实没有充分的理由将该方法设为私有。通常将类上的方法设为私有,以防止直接引用该类实例的人调用该方法。有了演员实例,没有人会直接引用该演员类的实例。您可以与该actor类的实例进行通信的所有ActorRef内容都是一个轻量级代理,它只允许您通过发送要onReceive通过邮箱处理的消息来进行通信。一个ActorRef不公开该actor类的任何内部状态或方法。这是演员系统的一大卖点。Actor 实例完全封装了它的内部状态和方法,保护它们不受外界影响,并且只允许这些内部事物响应接收消息而改变。这就是为什么似乎没有必要将该方法标记为私有的原因。

编辑

演员 IMO 的单元测试应始终通过receive功能。如果您有一些内部方法然后由 in 中的处理调用,则receive不应专注于孤立地测试这些方法,而应确保通过您在测试场景中传递的消息正确执行导致它们调用的路径。

在您的特定示例中,parse正在生成一条ParsedLog消息,然后将其发送给logWriter子演员。对我来说,知道parse按预期工作意味着断言logWriter收到了正确的消息。为了做到这一点,我将允许logWriter覆盖子项的创建,然后在测试代码中执行此操作,并用TestProbe. 然后,您可以expectMsg在该探针上使用以确保它收到预期的ParsedLog消息,从而还测试parse.

至于您关于将演员的真实业务转移到一个单独且更可测试的类然后从演员中调用它的其他评论,有些人这样做,所以它并非闻所未闻。我个人没有,但这只是我。如果这种方法对您有用,我认为它没有任何重大问题。

于 2015-04-02T11:15:11.800 回答
1

3 年前,在与演员打交道时,我遇到了同样的问题:我发现最好的方法是对演员传达责任的责任最小化。演员将接收消息并选择要调用的对象的方法或要发送的消息或要抛出的异常,仅此而已。这样,模拟参与者调用的服务和这些服务的输入将非常简单。

于 2015-04-02T02:46:25.520 回答