2015-04-01 263 views
2

我是新来的AKK,我正在尝试在Java上akka。我想了解演员中业务逻辑的单元测试。我读documentation和孤立的业务逻辑的演员中唯一的例子是: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: 忘了提,我也算是类似的选项:

  • 移动逻辑出来的演员完全进入辅助类。对阿卡来说常见的做法是?
  • Powermock ...但我试图避免它,如果重新设计,可以

回答

2

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

编辑演员的

单元测试,IMO,应该经常去通过receive功能。如果您有一些内部方法,然后通过receive中的处理调用,则不应该专注于单独测试这些方法,而是应确保通过在测试场景中传递的消息正确执行导致其调用的路径。

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

就你的其他评论而言,把演员的真实业务转移到一个单独的,更可测试的类中,然后从演员那里调用它,有些人会这样做,所以这并不是前所未闻的。我个人没有,但那只是我。如果这种方法适合你,我没有看到任何重大问题。

+0

非常感谢答案和这部分具体_ ActorRef不公开任何内部状态或该演员类的方法。这是演员system_的一大卖点之一,它不仅回答我的问题,而且帮助我更好地理解演员模型 – 2015-04-02 13:58:13

+0

你是对的(我投了你的答案),但你实际上没有回答任何具体问题。假设你不能改变给定的代码库,你将如何行动? – 2015-04-15 10:09:04

+0

@JamesSharp,我加了一点点细节。我没有直接回答这些问题,但我确实提供了我认为是测试演员的合理方法。希望这可以帮助。 – cmbaxter 2015-04-15 13:07:30

0

我3年前有同样的问题,涉及演员的时候:我发现最好的办法是有最低责任到演员传达信息的责任。 演员将收到消息并选择要调用的对象的方法或要发送的消息或要抛出的异常,就是这样。 通过这种方式,可以非常简单地模拟演员所调用的服务以及对这些服务的输入。