Reputation: 27
Consider following snippet; I have used WSClient here for some api calls via DI.
@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)
}
}
And I need to write unit test for get method. I'm doing the following thing
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`)
}
}
But the mocking fails and i get a null pointer exception in following line=>
val wsRequest = ws.url(baseURL+trailingURL).addQueryStringParameters("access_token" -> authToken).get()
Also when i'm using Json.toJson(wsResponse.body) i'm getting extra \ with each parameter in whole response. can anyone help me solving these two problems. Thanks.
Upvotes: 0
Views: 1698
Reputation: 4608
There is play-mockws, which exists solely because mocking a WSClient manually is really tedious.
// 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"
Further explanation:
Mocking a class / trait simply creates you an instance of that type out of thin air. You cannot do anything with that instance in general, calling any method on it will simply return null
. If your code under test calls methods of this object, you must stub those methods with answers (i.e. simply return a prepared value).
For WSClient
, this means you must stub the url
method since this will be called by any code doing HTTP requests. But this method returns a WSRequest
. So, you must mock this also... Any call on this new mock needs to be stubbed too, or else it will end in a NPE again. This really gets complicated very soon, and you probably don't understand your test code too well anymore. That's why play-mockws was created which makes it very easy to reason about calls to HTTP services in your Play application.
BTW, you may also combine play-mockws with the SIRD - String Interpolation Router DSL, which makes it even easier to extract values out of the routes or query parameters if you need to:
val ws = MockWS {
case GET(p"/$id") if id == "66405" =>
Action {
Results.Ok("...")
}
}
Upvotes: 2