Имитация актера и его ответ в модульном тесте

У меня есть актер, который инициируется внутри актера, я хочу издеваться над актером B, чтобы сообщение actB? GetDataForProcessing(значение) не отправляется в actB, и я могу протестировать функциональность модуля 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)?


person ARYA    schedule 23.05.2020    source источник


Ответы (1)


Добавьте в 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 путь к вашему актору при тестировании.

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")
}
person Ivan Stanislavciuc    schedule 24.05.2020