2

我正在使用 Scalamock 和 Scalatest 为 Play 应用程序编写单元测试。

我的原始代码如下所示:

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

在一部分中,我必须模拟对 Web 服务的外部调用,我正在尝试使用 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)

我能够成功地使用文件模拟发布请求,但我无法使用 JSON 模拟发布请求。

我尝试单独放置存根函数引用,例如:

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

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

我得到第二行的编译错误:Unable to resolve overloaded method post

我在这里想念什么?为什么我不能模拟一种重载方法而不模拟另一种?

4

4 回答 4

2

play.api.libs.ws.WSRequest有两种post方法(https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.libs.ws.WSRequest),采取:

  1. File
  2. T(其中T有一个隐含的界限Writeable

编译器失败,因为您尝试post使用仅与版本 1 匹配的单个参数进行调用。但是,JsValue不能用File.

您实际上想调用第二个版本,但这是一个带有两组参数的柯里化方法(尽管第二个是隐式的)。因此,您需要显式提供您期望的隐式模拟值,即

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

因此,一个可行的解决方案是:

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

老答案:

WSRequest提供 4 种post方法重载(https://www.playframework.com/documentation/2.5.8/api/java/play/libs/ws/WSRequest.html),采用:

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

您可以使用 a 进行模拟,File因为它匹配重载 4,但JsValue不匹配(这是 Play JSON 模型JsonNode的一部分,而是 Jackson JSON 模型的一部分)。如果您转换为Stringor JsonNode,那么它将解决正确的重载并编译。

于 2018-01-29T06:46:00.833 回答
1

我最好的猜测是,您WSRequest实际上play.libs.ws.WSRequest是 Java API 的一部分,而您应该使用play.api.libs.ws.WSRequestScala API。

该方法WSRequest.post存在BodyWritable[JsValue]并由 Scala API 隐式提供,WSBodyWritables但在 Java API 中不提供。

另一个原因可能是您JsValue的不是 aplay.api.libs.json.JsValue而是其他东西(例如spray.json.JsValue)。

于 2018-01-31T10:51:45.517 回答
1

我将引用一个示例,我已成功实现您想要做的事情,主要区别在于我使用mock而不是stub.

重要的部分是:

val ws = mock[WSClient]
val responseBody = "{...}"
...
"availableBooks" should {
  "retrieve available books" in {
    val expectedBooks = "BTC_DASH ETH_DASH USDT_LTC BNB_LTC".split(" ").map(Book.fromString).map(_.get).toList

    val request = mock[WSRequest]
    val response = mock[WSResponse]
    val json = Json.parse(responseBody)

    when(ws.url(anyString)).thenReturn(request)
    when(response.status).thenReturn(200)
    when(response.json).thenReturn(json)
    when(request.get()).thenReturn(Future.successful(response))

    whenReady(service.availableBooks()) { books =>
      books.size mustEqual expectedBooks.size

      books.sortBy(_.string) mustEqual expectedBooks.sortBy(_.string)
    }
  }
}

您可以在以下位置看到完整的测试:BinanceServiceSpec

于 2018-02-01T03:17:11.333 回答
0

如果您模拟 JsValue 的响应,我想它应该可以正常工作。

when(wsReq.post(Json.parse("""{...json request...}"""))).thenReturn(Future(wsResp))

这里Json.parse返回JsValue。Yo 应该在请求正文中传递您期望的 json 字符串。

于 2019-02-13T04:13:13.803 回答