无法使用 scalamock 模拟 WSRequest.post()
Cannot mock WSRequest.post() using scalamock
我正在使用 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)
我可以使用文件成功模拟 post 请求,但我无法使用 JSON.
模拟 post 请求
我尝试将存根函数引用分开放置,例如:
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
我在这里错过了什么?为什么我不能模拟一个重载方法而不能模拟另一个重载方法?
play.api.libs.ws.WSRequest
有两个post
方法(https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.libs.ws.WSRequest),采用:
File
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),取:
String
JsonNode
InputStream
File
您可以使用 File
进行模拟,因为它匹配重载 4,但 JsValue
不匹配(这是 Play JSON 模型的一部分,而 JsonNode
是 Jackson JSON 模型的一部分)。如果您转换为 String
或 JsonNode
,那么它将解析正确的重载并编译。
我最好的猜测是你的 WSRequest
实际上是一个 play.libs.ws.WSRequest
,它是 Java API 的一部分,你应该使用 play.api.libs.ws.WSRequest
是 Scala API.
方法 WSRequest.post
exists and BodyWritable[JsValue]
在 Scala API 中由 WSBodyWritables
隐式提供,但在 Java API.
中不提供
另一个原因可能是您的 JsValue
不是 play.api.libs.json.JsValue
而是其他东西(例如 spray.json.JsValue
)。
我会引用一个例子,我已经成功地实现了你想要做的事情,主要区别是我使用 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
我想如果你模拟一个 JsValue 的响应,它应该可以正常工作。
when(wsReq.post(Json.parse("""{...json request...}"""))).thenReturn(Future(wsResp))
此处Json.parse
returnsJsValue
。你应该在请求正文中传递你期望的 json 字符串。
我正在使用 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)
我可以使用文件成功模拟 post 请求,但我无法使用 JSON.
模拟 post 请求我尝试将存根函数引用分开放置,例如:
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
我在这里错过了什么?为什么我不能模拟一个重载方法而不能模拟另一个重载方法?
play.api.libs.ws.WSRequest
有两个post
方法(https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.libs.ws.WSRequest),采用:
File
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),取:
String
JsonNode
InputStream
File
您可以使用 File
进行模拟,因为它匹配重载 4,但 JsValue
不匹配(这是 Play JSON 模型的一部分,而 JsonNode
是 Jackson JSON 模型的一部分)。如果您转换为 String
或 JsonNode
,那么它将解析正确的重载并编译。
我最好的猜测是你的 WSRequest
实际上是一个 play.libs.ws.WSRequest
,它是 Java API 的一部分,你应该使用 play.api.libs.ws.WSRequest
是 Scala API.
方法 WSRequest.post
exists and BodyWritable[JsValue]
在 Scala API 中由 WSBodyWritables
隐式提供,但在 Java API.
另一个原因可能是您的 JsValue
不是 play.api.libs.json.JsValue
而是其他东西(例如 spray.json.JsValue
)。
我会引用一个例子,我已经成功地实现了你想要做的事情,主要区别是我使用 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
我想如果你模拟一个 JsValue 的响应,它应该可以正常工作。
when(wsReq.post(Json.parse("""{...json request...}"""))).thenReturn(Future(wsResp))
此处Json.parse
returnsJsValue
。你应该在请求正文中传递你期望的 json 字符串。