2015-12-18 28 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是正确的路,但不幸的是无法弄清楚如何正确使用它。