2015-12-18 27 views
3

如何測試給定的行爲是否會發送我期望的消息?測試Akka輸入的行爲

說,某些類型的三封郵件,一前一後...

通過定期參與者(非類型化)有是TestProbe從正規阿卡用的方法,如expectedMsg

http://doc.akka.io/api/akka/current/index.html#akka.testkit.TestProbe

阿卡式我仍然在撓我的頭。有一種叫做EffectfulActorContext的東西,但我不知道如何使用它。

說我寫一個簡單的PingPong服務,即賦予了許多n答覆與Pong(n)n -times。所以:

-> Ping(2) 
Pong(2) 
Pong(2) 
-> Ping(0) 
# nothing 
-> Ping(1) 
Pong(1) 

下面是這種行爲的外觀:

case class Ping(i: Int, replyTo: ActorRef[Pong]) 
case class Pong(i: Int) 

val pingPong: Behavior[Ping] = { 
    Static { 
     case Ping(i, replyTo) => (0 until i.max(0)).map(_=> replyTo ! Pong(i)) 
    } 
    } 

我哈克

現在,因爲我無法弄清楚如何使這項工作中,「黑客」,我我現在正在做的是讓演員總是回覆一份回覆列表。所以行爲是:

case class Ping(i: Int, replyTo: ActorRef[List[Pong]]) 
    case class Pong(i: Int) 

    val pingPong: Behavior[Ping] = { 
    Static { 
     case Ping(i, replyTo) => replyTo ! (0 until i.max(0)).map(_=>Pong(i)).toList 
    } 
    } 

鑑於這種哈克變化,測試人員很容易寫:

package com.test 

import akka.typed.AskPattern._ 
import akka.typed.ScalaDSL._ 
import akka.typed.{ActorRef, ActorSystem, Behavior, Props} 
import akka.util.Timeout 
import com.test.PingPong.{Ping, Pong} 
import org.scalatest.{FlatSpec, Matchers} 

import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.duration._ 
import scala.concurrent.{Await, Future} 

object PingPongTester { 
    /* Expect that the given messages arrived in order */ 
    def expectMsgs(i: Int, msgs: List[Pong]) = { 
    implicit val timeout: Timeout = 5 seconds 
    val pingPongBe: ActorSystem[Ping] = ActorSystem("pingPongTester", Props(PingPong.pingPong)) 

    val futures: Future[List[Pong]] = pingPongBe ? (Ping(i, _)) 
    for { 
     pongs <- futures 
     done <- { 
     for ((actual, expected) <- pongs.zip(msgs)) { 
      assert(actual == expected, s"Expected $expected, but received $actual") 
     } 
     assert(pongs.size == msgs.size, s"Expected ${msgs.size} messages, but received ${pongs.size}") 
     pingPongBe.terminate 
     } 
    } Await.ready(pingPongBe.whenTerminated, 5 seconds) 
    } 
} 


object PingPong { 
    case class Ping(i: Int, replyTo: ActorRef[List[Pong]]) 
    case class Pong(i: Int) 

    val pingPong: Behavior[Ping] = { 
    Static { 
     case Ping(i, replyTo) => replyTo ! (0 until i.max(0)).map(_=>Pong(i)).toList 
    } 
    } 
} 

class MainSpec extends FlatSpec with Matchers { 
    "PingPong" should "reply with empty when Pinged with zero" in { 
    PingPongTester.expectMsgs(0, List.empty) 
    } 
    it should "reply once when Pinged with one" in { 
    PingPongTester.expectMsgs(1, List(Pong(1))) 
    } 
    it should "reply with empty when Pinged with negative" in { 
    PingPongTester.expectMsgs(-1, List.empty) 
    } 
    it should "reply with as many pongs as Ping requested" in { 
    PingPongTester.expectMsgs(5, List(Pong(5), Pong(5), Pong(5), Pong(5), Pong(5))) 
    } 
} 

回答

1

我使用EffectfulActorContext測試我的阿卡類型的演員,這裏是一個未經考驗的例子根據你的問題。

注意:我還使用Akka類型測試用例中提供的guardian actor。

class Test extends TypedSpec{ 
    val system = ActorSystem("actor-system", Props(guardian())) 
    val ctx: EffectfulActorContext[Ping] = new EffectfulActorContext[Ping]("ping", Ping.props(), system) 

    //This will send the command to Ping Actor 
    ctx.run(Ping) 
    //This should get you the inbox of the Pong created inside the Ping actor. 
    val pongInbox = ctx.getInbox("pong") 
    assert(pongInbox.hasMessages) 
    val pongMessages = pongInbox.receiveAll() 
    pongMessages.size should be(1) //1 or whatever number of messages you expect 
    } 

編輯(一些更多的信息)

case class Pong(replyTo: ActorRef[Response]) 
val responseInbox: SyncInbox[Response] = Inbox.sync[Response]("responseInbox") 
Pong(responseInbox.ref) 
+0

不錯。 「receiveAll」是做什麼的?它是否等待? – drozzy

+0

我更新了代碼,因爲它引用了我的一些代碼,並修復了我用'ctx.getInbox(「pong」)'訪問'Pong'的收件箱的方式。您可以檢查編輯。調用'receiveAll'會給你'Pong'的收件箱中的所有信息,我不確定它是否等待,但我會研究它。 – hajime

0

我測試的初步做法是擴展性能等級:我需要在我的郵件中添加replyTo ActorRef我做了以下情況

class TestQueueBehavior[Protocol] extends Behavior[Protocol] { 
    val messages: BlockingQueue[Protocol] = new LinkedBlockingQueue[Protocol]() 

    val behavior: Protocol => Unit = { 
     (p: Protocol) => messages.put(p) 
    } 

    def pollMessage(timeout: FiniteDuration = 3.seconds): Protocol = { 
     messages.poll(timeout.toMillis, TimeUnit.MILLISECONDS) 
    } 

    override def management(ctx: ActorContext[Protocol], msg: Signal): Behavior[Protocol] = msg match { 
     case _ ⇒ ScalaDSL.Unhandled 
    } 

    override def message(ctx: ActorContext[Protocol], msg: Protocol): Behavior[Protocol] = msg match { 
     case p => 
     behavior(p) 
     Same 
    } 
    } 

然後我可以打電話給behavior.pollMessage(2.seconds) shouldBe somethingToCompareTo這是非常類似於使用TestProbe

雖然我認爲EffectfulActorContext是正確的路,但不幸的是無法弄清楚如何正確使用它。