单元测试Akka中的私有方法

时间:2015-04-01 21:53:00

标签: java unit-testing akka akka-testkit

我是akka的新手,我在java上尝试akka。我想了解演员内部业务逻辑的单元测试。我读了documentation,演员中唯一的隔离业务逻辑示例是:

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收到正确的解析消息
  3. 我的问题:

    1. 选项#1有任何缺点吗?假设演员只通过消息进行通信,封装和干净的开放接口不太重要吗?
    2. 如果我尝试使用选项#2,是否有办法捕获从测试下游的actor发送的消息(测试LogRowParser并捕获LogWriter)?我查看了JavaTestKit上的各种示例,但所有这些示例都捕获了回复给发送者的消息,没有一个会显示如何拦截发送给新玩家的消息。
    3. 我还有其他选择吗?
    4. 谢谢!

      UPD: 忘了提一下我也考虑了以下选项:

      • 将演员完全转移到辅助课程中。这是akka的常见做法吗?
      • Powermock ......但如果可以重新设计,我试图避免它

2 个答案:

答案 0 :(得分:2)

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

修改

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

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

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

答案 1 :(得分:0)

3年前,当与演员打交道时,我遇到了同样的问题:我发现最好的方法是对演员负责任的角色负责。 演员将收到该消息并选择要调用的对象方法或要发送的消息或抛出的异常以及它。 这样,模拟actor调用的服务和这些服务的输入将非常简单。