在play scala的单元测试中模拟WsClient

Mocking WsClient in unit tests in play scala

考虑以下片段;我在这里使用 WSClient 通过 DI 进行一些 api 调用。

@Singleton
class SampleService @Inject()(ws: WSClient) {
  def get(id:Long): JsValue ={
    val trailingURL = s"/$id".toString
    val wsRequest =  ws.url(baseURL+trailingURL).addQueryStringParameters("access_token" -> authToken).get()
    val wsResponse = Await.result(wsRequest, Duration.Inf)
    Json.toJson(wsResponse.body)
  }
}

而且我需要为 get 方法编写单元测试。我正在做以下事情

val mockedWS = mock[WSClient]
  val sparrowService = new SurveySparrowService(mockedWS)

  "get method" should  {
    "return a valid result with valid id" in {
      val result = sparrowService.get(66405)
      println(result)
      assert(result.toString == `the result i'll get`)
    }
  }

但是模拟失败了,我在下一行得到了一个空指针异常=>

val wsRequest =  ws.url(baseURL+trailingURL).addQueryStringParameters("access_token" -> authToken).get()

此外,当我使用 Json.toJson(wsResponse.body) 时,整个响应中的每个参数都会得到额外的 \。 谁能帮我解决这两个问题。谢谢

存在 play-mockws,它存在的唯一原因是手动模拟 WSClient 非常乏味。

// simulation of a GET request to http://dns/url
val ws = MockWS {
  case (GET, "http://dns/url") => Action { Ok("http response") }
}

await(ws.url("http://dns/url").get()).body == "http response"

进一步说明:

模拟一个 class / trait 只是凭空为你创建一个该类型的实例。一般来说,你不能对该实例做任何事情,调用它的任何方法只会 return null。如果您的测试代码调用此对象的方法,您必须用答案存根这些方法(即简单地 return 一个准备好的值)。

对于 WSClient,这意味着您必须存根 url 方法,因为任何执行 HTTP 请求的代码都会调用该方法。但是这个方法return是WSRequest。所以,你也必须模拟这个……对这个新模拟的任何调用也需要被存根,否则它将再次以 NPE 结束。这很快就会变得很复杂,您可能不再太了解您的测试代码。这就是创建 play-mockws 的原因,这使得在您的 Play 应用程序中推断对 HTTP 服务的调用变得非常容易。


顺便说一句,您还可以将 play-mockws 与 SIRD - String Interpolation Router DSL 结合使用,这使得在需要时更容易从路由或查询参数中提取值:

val ws = MockWS {
  case GET(p"/$id") if id == "66405" =>
    Action {
      Results.Ok("...")
    }
}