2015-04-01 211 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年前有同樣的問題,涉及演員的時候:我發現最好的辦法是有最低責任到演員傳達信息的責任。 演員將收到消息並選擇要調用的對象的方法或要發送的消息或要拋出的異常,就是這樣。 通過這種方式,可以非常簡單地模擬演員所調用的服務以及對這些服務的輸入。