Search code examples
scalaunit-testingplayframeworkspecificationsplayframework-2.5

How to test a Scala Play Framework websocket?


If I have a websocket like the following:

def websocket: WebSocket = WebSocket.accept[String, String] { _ =>
  ActorFlow.actorRef(out => LightWebSocketActor.props(out))
}

For reference, this is the LightWebSocketActor:

class LightWebSocketActor(out: ActorRef) extends Actor {
  val topic: String = service.topic

  override def receive: Receive = {
    case message: String =>
      play.Logger.debug(s"Message: $message")
      PublishService.publish("true")
      out ! message
  }
}

object LightWebSocketActor {
  var list: ListBuffer[ActorRef] = ListBuffer.empty[ActorRef]
  def props(out: ActorRef): Props = {
    list += out
    Props(new LightSocketActor(out))
  }

  def sendMessage(message: String): Unit = {
    list.foreach(_ ! message)
  }
}

This is using the akka websocket approach.

  • How should a test for this kind of controller be created?
  • How should I send information an expect a response?
  • What kind of information should be sent in the fake request?

For example I have this test for a regular html-returning controller:

"Application" should {
  "render the index page" in new WithApplication {
    val home = route(app, FakeRequest(GET, "/")).get
    status(home) must equalTo(OK)
    contentType(home) must beSome.which(_ == "text/html")
    contentAsString(home) must contain ("shouts out")
  }
}

Solution

  • Play 2.6

    I followed this Example: play-scala-websocket-example

    Main steps:

    Create or provide a WebSocketClient that you can use in your
    tests.

    Create the client:

    val asyncHttpClient: AsyncHttpClient = wsClient.underlying[AsyncHttpClient]
    val webSocketClient = new WebSocketClient(asyncHttpClient)
    

    Connect to the serverURL:

    val listener = new WebSocketClient.LoggingListener(message => queue.put(message))
    val completionStage = webSocketClient.call(serverURL, origin, listener)
    val f = FutureConverters.toScala(completionStage)
    

    Test the Messages sent by the Server:

    whenReady(f, timeout = Timeout(1.second)) { webSocket =>
      await().until(() => webSocket.isOpen && queue.peek() != null)
    
      checkMsg1(queue.take())
      checkMsg2(queue.take())
      assert(queue.isEmpty)
    }
    

    For example, like:

      private def checkMsg1(msg: String) {
        val json: JsValue = Json.parse(msg)
        json.validate[AdapterMsg] match {
          case JsSuccess(AdapterNotRunning(None), _) => // ok
          case other => fail(s"Unexpected result: $other")
        }
      }
    

    The whole example can be found here: scala-adapters (JobCockpitControllerSpec)