当前位置: 首页 > 知识库问答 >
问题:

测试Akka类型行为

韩鸿波
2023-03-14

我如何测试给定的行为是否发送了我期望的消息

比如说,三条某种类型的信息,一条接一条...

对于常规参与者(非类型化),有来自常规Akka的TestProbe,方法如expectedmsg:

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

有了Akka型的我还在挠头。有一个叫做EffectfulActorContext的东西,但我不知道如何使用它。

假设我正在编写一个简单的pingpong服务,给定一个数字n将以pong(n)n-次来回复。所以:

-> 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)))
  }
}

共有2个答案

朱伯寅
2023-03-14

我最初的测试方法是扩展行为类

  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是正确的方法,但不幸的是,我不知道如何正确地使用它。

柴凌
2023-03-14

我正在使用EffectfulActorContext来测试我的Akka类型的actor,下面是一个基于您的问题的未经测试的示例。

注意:我还使用了Akka类型的测试用例中提供的guardian执行元。

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
  }

编辑(更多信息):如果需要在消息中添加replytoActorRef,我会执行以下操作:

case class Pong(replyTo: ActorRef[Response])
val responseInbox: SyncInbox[Response] = Inbox.sync[Response]("responseInbox")
Pong(responseInbox.ref) 
 类似资料:
  • 我正在使用TestKit测试一个Scala项目的一些类,这个项目涉及Akka Actor,我遇到了这个问题: 所讨论的类如下所示: 我以前没有这个问题,因为我有

  • 如果你遵循 的例子,并使用文件来指定你的项目测试需求,记住你还需要添加节点类型到你的依赖项。

  • 我试图用akka-http测试一个TypedActor,但在尝试创建测试用例时遇到了一些问题。为了测试TypedActor,我将编写以下规范... 但是,当我必须编写一个与HTTP/+WS路由一起使用的TypedActor时,我无法编写... 我如何才能编写一个同时使用这两种测试呢? 请指教。

  • 假设我正在为Akka(类型化)执行元定义一个行为,该行为将执行并发计算,将其结果报告给生成它的执行元,然后停止。 如果我用计算的所有输入初始化这个actor,加上对它的“父级”的引用,那么它将永远不需要接收任何类型的传入消息。 我将使用创建此行为,向它传递一个执行计算的函数,然后返回。 Akka(Typed)要求我为将返回此行为的函数提供某种的结果类型。虽然我可以为分配行为将发送的结果消息的类型,

  • 本文向大家介绍 测试类型有哪些?相关面试题,主要包含被问及 测试类型有哪些?时的应答技巧和注意事项,需要的朋友参考一下 黑盒 白盒 灰盒