模拟演员及其在单元测试中的反应
Mock actor and its response in unit test
我有一个在 actor 内部启动的 actor,我想模拟 actorB 这样
消息演员B? GetDataForProcessing(value) 未发送到 actorB,我可以测试 ActorExp 的单元功能。
我的演员是:
class ActorExp(a: ARepo) extends Actor{
lazy val actorB = context.system.actorSelection("user/ActorB")
def receive: Receive = {
case m @ GetData(value) =>
sender() ! getData(value)
}
def getData(value:String) = {
val res = actorB ? GetDataForProcessing(value)
res map {
...
}
}
}
class ActorB(repoB: BRepo) extends Actor{...}
我正在尝试编写的测试是:
class ActorExpSpecSpec
extends TestKit(ActorSystem("ActorExpSpecSpec"))
with WordSpecLike
with Matchers
with JsonSupport
with MockitoSugar
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll: Unit = TestKit.shutdownActorSystem(system)
implicit val futureDuration: FiniteDuration = 60.seconds
implicit val timeout: Timeout = 10.seconds
val mockedRepoA = mock[ARepo]
val actorA: ActorRef = TestActorRef(ActorExp.props(mockedRepoA))
"ActorExpSpecSpec" should {
"be able to data" in {
val action = (actorA ? GetData("value")).mapTo[Seq[String]]
val result = Await.result(action, 10.seconds)
result should equal("yes")
}
}
}
现在我收到询问超时异常,因为 actorB
未初始化,因为它需要某些参数,我不想测试 actorB
。在上述场景中有什么方法可以模拟 actorB
及其消息 actorB ? GetDataForProcessing(value)
?
向 ActorExp
添加额外的构造函数参数,定义演员选择路径 user/ActorB
并在您的测试表单中提供它 TestProbe().ref.path
这里有一些简化的例子:
class ParentActor(actorPath: String) extends Actor {
private val selected = context.system.actorSelection(actorPath)
override def receive: Receive = {
case msg: String =>
val replyTo = sender()
//some irrelevant logic to test
(selected ? msg).mapTo[String].onComplete {
case Success(v) => replyTo ! v
case Failure(ex) => throw ex
}
}
}
现在,您可以在测试时像这样提供 TestProbe
进入您的 actor 的路径。
val testProbe = TestProbe()
val actor = system.actorOf(
Props(new ParentActor(testProbe.ref.path.toStringWithoutAddress))
)
val reply = actor ? "Hello"
testProbe.expectMsg("Hello")
testProbe.reply("Hi")
reply.map { res =>
assert(res == "Hi")
}
我有一个在 actor 内部启动的 actor,我想模拟 actorB 这样 消息演员B? GetDataForProcessing(value) 未发送到 actorB,我可以测试 ActorExp 的单元功能。
我的演员是:
class ActorExp(a: ARepo) extends Actor{
lazy val actorB = context.system.actorSelection("user/ActorB")
def receive: Receive = {
case m @ GetData(value) =>
sender() ! getData(value)
}
def getData(value:String) = {
val res = actorB ? GetDataForProcessing(value)
res map {
...
}
}
}
class ActorB(repoB: BRepo) extends Actor{...}
我正在尝试编写的测试是:
class ActorExpSpecSpec
extends TestKit(ActorSystem("ActorExpSpecSpec"))
with WordSpecLike
with Matchers
with JsonSupport
with MockitoSugar
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll: Unit = TestKit.shutdownActorSystem(system)
implicit val futureDuration: FiniteDuration = 60.seconds
implicit val timeout: Timeout = 10.seconds
val mockedRepoA = mock[ARepo]
val actorA: ActorRef = TestActorRef(ActorExp.props(mockedRepoA))
"ActorExpSpecSpec" should {
"be able to data" in {
val action = (actorA ? GetData("value")).mapTo[Seq[String]]
val result = Await.result(action, 10.seconds)
result should equal("yes")
}
}
}
现在我收到询问超时异常,因为 actorB
未初始化,因为它需要某些参数,我不想测试 actorB
。在上述场景中有什么方法可以模拟 actorB
及其消息 actorB ? GetDataForProcessing(value)
?
向 ActorExp
添加额外的构造函数参数,定义演员选择路径 user/ActorB
并在您的测试表单中提供它 TestProbe().ref.path
这里有一些简化的例子:
class ParentActor(actorPath: String) extends Actor {
private val selected = context.system.actorSelection(actorPath)
override def receive: Receive = {
case msg: String =>
val replyTo = sender()
//some irrelevant logic to test
(selected ? msg).mapTo[String].onComplete {
case Success(v) => replyTo ! v
case Failure(ex) => throw ex
}
}
}
现在,您可以在测试时像这样提供 TestProbe
进入您的 actor 的路径。
val testProbe = TestProbe()
val actor = system.actorOf(
Props(new ParentActor(testProbe.ref.path.toStringWithoutAddress))
)
val reply = actor ? "Hello"
testProbe.expectMsg("Hello")
testProbe.reply("Hi")
reply.map { res =>
assert(res == "Hi")
}