Модульные тесты для веб-сокетов с использованием akka http - PullRequest
0 голосов
/ 05 июня 2018

Я реализовал веб-сокеты, используя Akka HTTP.Я использую данные из Кафки и отправляю уведомления через веб-сокеты.Функциональность работает нормально, но Я застрял в тестовых случаях .

 ` private def loginNotificationRoute(): Route = {
    cors() {
      pathPrefix("notifications" / Segment) { userName =>
        get {
          handleWebSocketMessages(notificationClientFlow(userName))
        }
      }
    }
  }`

Активатор уведомлений читает уведомления из базы данных и отправляет данные обратно клиенту.

`  def notificationClientFlow(userName: String): Flow[Message, Message, NotUsed] = {
    info(s"Connection Request accepted for user $userName")
    val notificationActor = actorSystem.actorOf(NotificationActor.props(userName, jsonHelper))

    val incomingMessages: Sink[Message, NotUsed] =
      Flow[Message].map {
        case TextMessage.Strict(text) => NotificationActor.IncomingMessage(text)
      }.to(Sink.actorRef(notificationActor, PoisonPill))

    val outgoingMessages: Source[Message, NotUsed] =
      Source
        .actorRef[NotificationActor.ResponseType](BUFFER_SIZE, OverflowStrategy.fail)
        .mapMaterializedValue { outgoingActor =>
          notificationActor ! NotificationActor.Connected(outgoingActor)
          NotUsed
        }
        .map {
          notificationResponse: NotificationActor.ResponseType =>
            info(s"sending notification ${notificationResponse.action}")
            TextMessage.Strict(jsonHelper.write(notificationResponse))
        }

    Flow.fromSinkAndSource(incomingMessages, outgoingMessages)
  }`

1 Ответ

0 голосов
/ 05 июня 2018

Перепроектирование зависимостей

На мой взгляд, самым большим недостатком вашего текущего дизайна является то, что существует внутренняя зависимость от конкретного actorSystem;ошибочная строка кода:

//actorSystem comes from the outside world!!!
val notificationActor = actorSystem.actorOf(NotificationActor.props(userName, jsonHelper))

Метод notificationClientFlow должен быть перепараметрирован, чтобы все зависимости были явно указаны в аргументе.И не должно быть зависимости от всего ActorSystem, вместо этого она должна быть уменьшена до ActorRef:

def notificationClientFlow(userName: String, 
                           notificationActor : ActorRef): Flow[Message, Message, NotUsed] = {
  //notificationActor is now passed in
  //val notificationActor = actorSystem.actorOf(NotificationActor.props(userName, jsonHelper))
}

Это потребует аналогичного явного объявления в методе создания Route:

private def loginNotificationRoute(notificationActor : ActorRef)() : Route = {
  ...
  handleWebSocketMessages(notificationClientFlow(userName, notificationActor))
}

Тестирование

Теперь тестирование можно выполнить с помощью TestKit:

class MySpec() extends TestKit(ActorSystem("MySpec")) {

  val userName = "testUser"

  val notificationActor = 
    system.actorOf(NotificationActor.props(userName, jsonHelper))

  val testFlow = notificationClientFlow(userName, notificationActor)

}

Такжепоскольку мы явно передали ActorRef, а не только ActorSystem, мы можем использовать другие расширенные функции тестирования, такие как зонды :

val probe = TestProbe()

val testProbeFlow = notificationClientFlow(userName, probe.ref)

Вышеупомянутая методика аналогично применима ктестирование маршрута с использованием стандартных методик :

val testRoute = loginNotificationRoute(testProbe)

Get() ~> testRoute() ~> check {
  //testing assertions here
}
...