scalaplayframeworkscalamockplay-ws

Cannot mock WSRequest.post() using scalamock


I am writing unit tests for Play application using Scalamock and Scalatest.

My original code looks like:

// Here ws is an injected WSClient
val req = Json.toJson(someRequestObject)
val resp: Future[WSResponse] = ws.url(remoteURL).post(Json.toJson(req))

In a part I have to mock external calls to a web service, which I am trying to do using scalamock:

ws = stub[WSClient]
wsReq = stub[WSRequest]
wsResp = stub[WSResponse]

ws.url _ when(*) returns wsReq
wsReq.withRequestTimeout _ when(*) returns wsReq
(wsReq.post (_: java.io.File)).when(*) returns Future(wsResp)

I am successfully able to mock post requests using a file, but I cannot mock post requests using JSON.

I tried putting stub function references separately like:

val f: StubFunction1[java.io.File, Future[WSResponse]] = wsReq.post (_: java.io.File)

val j: StubFunction1[JsValue, Future[WSResponse]] = wsReq.post(_: JsValue)

I get the compile error for second line: Unable to resolve overloaded method post

What am I missing here? Why cannot I mock one overloaded method but not the other one?


Solution

  • play.api.libs.ws.WSRequest has two post methods (https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.libs.ws.WSRequest), taking:

    1. File
    2. T (where T has an implicit bounds on Writeable)

    The compiler is failing because you are trying to calling post with a single parameter, which only matches version 1. However, JsValue cannot be substituted with File.

    You actually want to call the 2nd version, but this is a curried method that takes two sets of parameters (albeit the 2nd are implicit). Therefore you need to explicitly provide the mock value that you expect for the implicit, i.e.

    val j: StubFunction1[JsValue, Future[WSResponse]] = wsReq.post(_: JsValue)(implicitly[Writeable[JsValue]])

    Therefore a working solution would be:

    (wsReq.post(_)(_)).when(*) returns Future(wsResp)

    Old answer:

    WSRequest provides 4 overloads of post method (https://www.playframework.com/documentation/2.5.8/api/java/play/libs/ws/WSRequest.html), taking:

    1. String
    2. JsonNode
    3. InputStream
    4. File

    You can mock with a File because it matches overload 4, but JsValue does not match (this is part of the Play JSON model, whereas JsonNode is part of the Jackson JSON model). If you convert to a String or JsonNode, then it will resolve the correct overload and compile.