I have a controller that sends a chunked response:
def streamDatase2t(query:String): Action[AnyContent] = Action.async {
req =>
serivce.getIterator(query).map(res => {
Ok.chunked(Source.apply(res))
})
}
When I try to inspect the returned content in the controller spec I get an exception:
"return 200 response with the content of the iterator" in {
when(serivce.getIterator
(Matchers.any[Request.DatasetLoad],
Matchers.any[ResponseFormat], Matchers.any[Int]))
.thenReturn(Future.successful(new FakeIterable(List("One", "Two", "Three").iterator)))
val fakeRequest = FakeRequest.apply("GET", s"/data")
val result = Helpers.route(fakeApp, fakeRequest).get
checkStatus(result, OK)
contentAsString(result) // <-- exception here !
}
Exception:
NoMaterializer cannot materialize
java.lang.UnsupportedOperationException: NoMaterializer cannot materialize
at play.api.test.NoMaterializer$.materialize(Helpers.scala:732)
at akka.stream.scaladsl.RunnableGraph.run(Flow.scala:629)
at akka.stream.scaladsl.Source.runWith(Source.scala:106)
at akka.stream.scaladsl.Source.runFold(Source.scala:117)
at play.api.http.HttpEntity.consumeData(HttpEntity.scala:49)
at play.api.http.HttpEntity.consumeData$(HttpEntity.scala:48)
at play.api.http.HttpEntity$Chunked.consumeData(HttpEntity.scala:117)
at play.api.test.ResultExtractors.contentAsBytes(Helpers.scala:381)
at play.api.test.ResultExtractors.contentAsBytes$(Helpers.scala:379)
at play.api.test.Helpers$.contentAsBytes(Helpers.scala:676)
As the Exception states NoMaterializer cannot materialize you may need to add a Materializer:
implicit lazy val mat = ActorMaterializer()
implicit lazy val ec = instanceOf[ExecutionContext]
contentAsString has NoMaterializer as the default argument
def contentAsString(of: Future[Result])(implicit timeout: Timeout, mat: Materializer = NoMaterializer): String
NoMaterializer just throws UnsupportedOperationException for everything so try providing your own
implicit val actorSystem = ActorSystem("test")
implicit val materializer = ActorMaterializer()
play-scala-streaming-example demonstrates how we might write a test for streaming controller.
Addressing the comment, consider the following two routes which illustrate the difference between a strict and non-strict (chunked, streamed) body
def nonStrictBody = Action {
val source = Source.apply(List("woo", "h", "oo"))
Ok.chunked(source)
}
def strictBody = Action {
Ok("woohoo")
}
When calling contentAsString on a strict body, then materializer will not be used, hence NoMaterializer is sufficient
In 99% of cases, when running tests against the result body, you don't actually need a materializer since it's a strict body. So, rather than always requiring an implicit materializer, we use one if provided, otherwise we have a default one that simply throws an exception if used.
However when calling contentAsString on a chunked or streamed body, as it is the case in the nonStrictBody route, then we need to provide a proper Materializer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With