How to write Unit Test for HTTPBuilder using SPOCK framework? - unit-testing

I want the Unit Test to go through both, Success and Failure execution paths. How to make the test case to go to Success or Failure path?
void addRespondents()
{
http.request(POST, TEXT) {
uri.path = PATH
headers.Cookie = novaAuthentication
headers.Accept = 'application/json'
headers.ContentType = 'application/json'
body = respondentString
response.success = { resp, json ->
statusCode = 2
}
response.failure = { resp, json ->
if(resp.status == 400) {
statusCode = 3
def parsedJson = new JsonSlurper().parse(json)
}else{
autoCreditResponse = createErrorResponse(resp)
}
}
}
}

OK, it seems you use this library:
<dependency>
<groupId>org.codehaus.groovy.modules.http-builder</groupId>
<artifactId>http-builder</artifactId>
<version>0.7.1</version>
</dependency>
Because I never used HTTPBuilder before and it looks like a nice tool when using Groovy, I played around with it a bit, replicating your use case, but converting it into a full MCVE. I have to admit that testability for this library is in bad shape. Even the tests for the library itself are no proper unit tests, but rather integration tests, actually performing network requests instead of mocking them. The tool itself also contains to test mocks or hints about how to test.
Because the functionality heavily relies on dynamically binding variables in closures, mock-testing is somewhat ugly and I had to look into the tool's source code in order to pull it off. Nice black-box testing is basically impossible, but here is how you can inject a mock HTTP client returning a predefined mock response which contains enough information not to derail the application code:
Class under test
As you can see, I added enough data in the class to be able to run it and do something meaningful. The fact that your method returns void instead of a testable result and we only have to rely on testing side effects, does not make testing easier.
package de.scrum_master.stackoverflow.q68093910
import groovy.json.JsonSlurper
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator
import static groovyx.net.http.ContentType.TEXT
import static groovyx.net.http.Method.POST
class JsonApiClient {
HTTPBuilder http = new HTTPBuilder("https://jsonplaceholder.typicode.com")
String PATH = "/users"
String novaAuthentication = ''
String respondentString = ''
String autoCreditResponse = ''
int statusCode
JsonSlurper jsonSlurper = new JsonSlurper()
void addRespondents() {
http.request(POST, TEXT) {
uri.path = PATH
headers.Cookie = novaAuthentication
headers.Accept = 'application/json'
headers.ContentType = 'application/json'
body = respondentString
response.success = { resp, json ->
println "Success -> ${jsonSlurper.parse(json)}"
statusCode = 2
}
response.failure = { resp, json ->
if (resp.status == 400) {
println "Error 400 -> ${jsonSlurper.parse(json)}"
statusCode = 3
}
else {
println "Other error -> ${jsonSlurper.parse(json)}"
autoCreditResponse = createErrorResponse(resp)
}
}
}
}
String createErrorResponse(HttpResponseDecorator responseDecorator) {
"ERROR"
}
}
Spock specification
This spec covers all 3 cases for responses in the above code, using an unrolled test which returns different status codes.
Because the method under test returns void, I decided to verify the side effect that HTTPBuilder.request was actually called. In order to do this, I had to use a Spy on the HTTPBuilder. Testing for this side effect is optional, then you do not need the spy.
package de.scrum_master.stackoverflow.q68093910
import groovyx.net.http.HTTPBuilder
import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
import org.apache.http.client.ResponseHandler
import org.apache.http.entity.StringEntity
import org.apache.http.message.BasicHttpResponse
import org.apache.http.message.BasicStatusLine
import spock.lang.Specification
import spock.lang.Unroll
import static groovyx.net.http.ContentType.TEXT
import static groovyx.net.http.Method.POST
import static org.apache.http.HttpVersion.HTTP_1_1
class JsonApiClientTest extends Specification {
#Unroll
def "verify status code #statusCode"() {
given: "a JSON response"
HttpResponse response = new BasicHttpResponse(
new BasicStatusLine(HTTP_1_1, statusCode, "my reason")
)
def json = "{ \"name\" : \"JSON-$statusCode\" }"
response.setEntity(new StringEntity(json))
and: "a mock HTTP client returning the JSON response"
HttpClient httpClient = Mock() {
execute(_, _ as ResponseHandler, _) >> { List args ->
(args[1] as ResponseHandler).handleResponse(response)
}
}
and: "an HTTP builder spy using the mock HTTP client"
HTTPBuilder httpBuilder = Spy(constructorArgs: ["https://foo.bar"])
httpBuilder.setClient(httpClient)
and: "a JSON API client using the HTTP builder spy"
def builderUser = new JsonApiClient(http: httpBuilder)
when: "calling 'addRespondents'"
builderUser.addRespondents()
then: "'HTTPBuilder.request' was called as expected"
1 * httpBuilder.request(POST, TEXT, _)
where:
statusCode << [200, 400, 404]
}
}
If you have used Spock for a while, probably I do not need to explain much. If you are a Spock or mock testing beginner, probably this is a bit too complex. But FWIW, I hope that if you study the code, you can wrap your head around how I did it. I tried to use Spock label comments in order to explain it.
Console log
The console log indicates that all 3 execution paths are covered by the specification:
Success -> [name:JSON-200]
Error 400 -> [name:JSON-400]
Other error -> [name:JSON-404]
If you use a code coverage tool, of course you do not need the log statements I inserted into the application code. They are just for demonstration purposes.
Verifying the result of http.request(POST, TEXT) {...}
In order to circumvent the fact that your method returns void, you can save the result of HTTPBuilder.request(..) by stubbing the method call in the spy interaction, passing through the original result at first, but also checking for the expected result.
Simply add def actualResult somewhere in the given ... and blocks (in when it is too late), then assign the result of callRealMethod() to it and then compare to expectedResult like this:
and: "a JSON API client using the HTTP builder spy"
def builderUser = new JsonApiClient(http: httpBuilder)
def actualResult
when: "calling 'addRespondents'"
builderUser.addRespondents()
then: "'HTTPBuilder.request' was called as expected"
1 * httpBuilder.request(POST, TEXT, _) >> {
actualResult = callRealMethod()
}
actualResult == expectedResult
where:
statusCode << [200, 400, 404]
expectedResult << [2, 3, "ERROR"]
If you prefer a data table instead of data pipes, the where block looks like this:
where:
statusCode | expectedResult
200 | 2
400 | 3
404 | "ERROR"
I think this pretty much covers all that makes sense to test here.

Related

how to bind request body in Finch

Here is the code to bind request param to the router.
val testReader: Endpoint[Test] = Endpoint.derive[Test].fromParams
val test: Endpoint[String] = post("test" ? testReader) { t : Test => {
Created("OK")
}}
I am using the method fromParams. This method can bind request parameters in a very cool way. However, I dont konw which similiar way I can bind request body in the finch
Many thanks in advance
For the sake of providing a complete working example I'll assume a case class like this:
case class Test(foo: Int, bar: String)
And some requests like this:
import com.twitter.finagle.http.{ Method, Request, RequestBuilder }
import com.twitter.io.{ Buf, Reader }
val queryParamPost = Request(Method.Post, "/test?foo=1&bar=whatever")
val testJsonBuf = Buf.Utf8("""{ "foo": 1, "bar": "whatever" }""")
val bodyPost = RequestBuilder().url("http://localhost:8080/test").buildPost(testJsonBuf)
Now when you write the following…
import io.finch._
val testParams: Endpoint[Test] = Endpoint.derive[Test].fromParams
val test: Endpoint[Test] = post("test" ? testParams) { test: Test =>
Created(test)
}
What's happening is that Finch is using generic derivation (powered by Shapeless) to determine (at compile time) how to parse the query params as a Test. You can then test the endpoint like this:
import io.finch.circe._
import io.circe.generic.auto._
test.toService.apply(queryParamPost).onSuccess { response =>
println(s"$response: ${ response.contentString }")
}
Which will print:
Response("HTTP/1.1 Status(201)"): {"foo":1,"bar":"whatever"}
Here I'm using Circe's generic derivation to automatically encode the "created" Test as JSON for the response.
You can also use Circe to derive a reader for the request body:
val testBody: Endpoint[Test] = body.as[Test]
val test2: Endpoint[Test] = post("test" :: testBody) { test: Test =>
Created(test)
}
This is almost exactly the same as test above, but we're using body to get an Endpoint[String] that will read the request body and then as to specify that we want the content parsed as JSON and decoded as a Test value. We can test this new version like this:
test2.toService.apply(bodyPost).onSuccess { response =>
println(s"$response: ${ response.contentString }")
}
And we'll get the answer we expect again.
In general when you want to read a certain kind of information of an incoming request, you'll use one of the basic Endpoints provided by Finch (see the docs for a more complete list), and then use methods like as, map, etc. on the Endpoint to turn it into the shape you need.

How to mock a HttpServletRequest's BufferedReader payload in Grails/Spock

I have a Grails controller that expects an XML payload.
I fetch the XML payload like this in a Grails controller.
def xmlPayload = request.reader.text
That part works fine, but I'm struggling to mock this payload in a unit test.
I've tried both of the following, but the debugger is showing 'request.reader' to be null in both approaches.
Approach #1:
void "test controller method"(){
setup:
def mockBufferedReader = Mock( BufferedReader )
mockBufferedReader.getText() >> '<hello/>'
request.getReader() >> mockBufferedReader
....
Approach #2:
void "test controller method"(){
setup:
def mockBufferedReader = Mock( BufferedReader )
mockBufferedReader.getText() >> '<hello/>'
request.metaClass.getReader = { -> mockBufferedReader }
....
'request' in a unit test is a GrailsMockHttpServletRequest, so I presumed I could mock its methods like this (3rd line of both approaches), but so far no luck.
Thanks for any ideas.
You can do:
class EchoController {
def echo () {
render (request.reader.text)
}
}
#TestFor(EchoController)
class EchoSpec extends Specification {
def "echos XML data" () {
request.XML = '<hello/>'
when:
controller.echo ()
then:
response.text == '<hello/>'
}
}
See Testing XML and JSON Requests in Unit Testing Controllers.
If you only need to provide contents for a request, then you don't need to mock anything.
def "Spock works as expected"() {
given:
def request = new GrailsMockHttpServletRequest(content: '<hello/>')
when:
def result = request.getReader().getText()
then:
result == '<hello/>'
}
One purpose of such Mock classes (as in Spring Test etc.) is to avoid explicit mocking with external libraries.

Unit testing controllers in Play framework with SecureSocial

I am trying to invent some kind of mocking SecureSocial action generators, or SecureSocial itself to be able to unit-test controller methods.
I've found some approaches, like Unit-testing methods secured with Securesocial annotation and Testing a Play2 application with SecureSocial using dependency injection but the thing is, that in that questions authors, in fact, don't do unit testing, but integration testing.
My unit tests look like this:
trait MockDaoProvider extends IDaoProvider {
def entityDao = entityDaoMock
}
val controller = new MyController with MockDaoProvider
"MyController.list" should {
"return an OK" in {
entityDaoMock.list().returns(List())
val result = controller.list()(FakeRequest())
status(result) must equalTo(OK)
}
}
As one can see, I mocked dependencies to isolate and test the behavior that controller method actually does.
Everything was OK until I used SecuredAction from securesocial for MyController.list method. Now I get an exception, and the test fails. I have no idea how I could mock, stub or override SecuredAction and UserAwareAction objects from securesocial. Still I don't want to convert my tests into route(...) tests. They are intended to test only the controller's behavior.
Have someone encountered the same problem? May be there are any hints how it could be solved?
PS: Play framework 2.2.1, securesocial - 2.1.2
It seem like the author of the code really hasn't emphasized testability, which has forced users to come up with their own novel solutions. This one by user jeantil could be helpful:
class FakeAuthenticatorStore(app:Application) extends AuthenticatorStore(app) {
var authenticator:Option[Authenticator] = None
def save(authenticator: Authenticator): Either[Error, Unit] = {
this.authenticator=Some(authenticator)
Right()
}
def find(id: String): Either[Error, Option[Authenticator]] = {
Some(authenticator.filter(_.id == id)).toRight(new Error("no such authenticator"))
}
def delete(id: String): Either[Error, Unit] = {
this.authenticator=None
Right()
}
}
abstract class WithLoggedUser(val user:User,override val app: FakeApplication = FakeApplication()) extends WithApplication(app) with Mockito{
lazy val mockUserService=mock[UserService]
val identity=IdentityUser(Defaults.googleId, user)
import helpers._
import TestUsers._
def cookie=Authenticator.create(identity) match {
case Right(authenticator) => authenticator.toCookie
}
override def around[T: AsResult](t: =>T): execute.Result = super.around {
mockUserService.find(Defaults.googleId) returns Some(identity)
UserService.setService(mockUserService)
t
}
}
val excludedPlugins=List(
,"service.login.MongoUserService"
,"securesocial.core.DefaultAuthenticatorStore"
)
val includedPlugins = List(
"helpers.FakeAuthenticatorStore"
)
def minimalApp = FakeApplication(withGlobal =minimalGlobal, withoutPlugins=excludedPlugins,additionalPlugins = includedPlugins)
which then allows testing like this
"create a new user password " in new WithLoggedUser(socialUser,minimalApp) {
val controller = new TestController
val req: Request[AnyContent] = FakeRequest().
withHeaders((HeaderNames.CONTENT_TYPE, "application/x-www-form-urlencoded")).
withCookies(cookie) // Fake cookie from the WithloggedUser trait
val requestBody = Enumerator("password=foobarkix".getBytes) andThen Enumerator.eof
val result = requestBody |>>> controller.create.apply(req)
val actual: Int= status(result)
actual must be equalTo 201
}
After some thinking, probing and experimenting I've ended up with an elegant solution. The solution relies on "cake pattern" of dependency injection. Like this:
Code in controller:
trait AbstractSecurity {
def Secured(action: SecuredRequest[AnyContent] => Result): Action[AnyContent]
}
trait SecureSocialSecurity extends AbstractSecurity with securesocial.core.SecureSocial {
def Secured(action: SecuredRequest[AnyContent] => Result): Action[AnyContent] = SecuredAction { action }
}
abstract class MyController extends Controller with AbstractSecurity {
def entityDao: IEntityDao
def list = Secured { request =>
Ok(
JsArray(entityDao.list())
)
}
}
object MyController extends MyController with PsqlDaoProvider with SecureSocialSecurity
And test code:
trait MockedSecurity extends AbstractSecurity {
val user = Account(NotAssigned, IdentityId("test", "userpass"), "Test", "User",
"Test user", Some("test#user.com"), AuthenticationMethod("userPassword"))
def Secured(action: SecuredRequest[AnyContent] => play.api.mvc.Result): Action[AnyContent] = Action { request =>
action(new SecuredRequest(user, request))
}
}
val controller = new MyController with MockDaoProvider with MockedSecurity
"IssueController.list" should {
"return an OK" in {
entityDaoMock.list().returns(List())
val result = controller.list()(FakeRequest())
status(result) must equalTo(OK)
}
}
Still there is a drawback - the tests depends on securesocial classes as well... but... is it really a drawback?
I don't know how this approach will work in more complex situations, we'll see.

How to mock spray-client response

I have a simple spray client :
val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]
val responseFuture = pipeline {Get("http://maps.googleapis.com/maps/api/elevation/jsonlocations=27.988056,86.925278&sensor=false") }
responseFuture onComplete {
case Success(GoogleApiResult(_, Elevation(_, elevation) :: _)) =>
log.info("The elevation of Mt. Everest is: {} m", elevation)
shutdown()
case Failure(error) =>
log.error(error, "Couldn't get elevation")
shutdown()
}
Full code can be found here.
I want to mock the response of the server to test the logic in the Success and Failure cases. The only relevant information i found was here but I haven't been able to use the cake pattern to mock the sendReceive method.
Any suggestion or example would be greatly appreciated.
Here's an example of one way to mock it using specs2 for the test spec and mockito for the mocking. First, the Main object refactored into a class setup for mocking:
class ElevationClient{
// we need an ActorSystem to host our application in
implicit val system = ActorSystem("simple-spray-client")
import system.dispatcher // execution context for futures below
val log = Logging(system, getClass)
log.info("Requesting the elevation of Mt. Everest from Googles Elevation API...")
import ElevationJsonProtocol._
import SprayJsonSupport._
def sendAndReceive = sendReceive
def elavation = {
val pipeline = sendAndReceive ~> unmarshal[GoogleApiResult[Elevation]]
pipeline {
Get("http://maps.googleapis.com/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false")
}
}
def shutdown(): Unit = {
IO(Http).ask(Http.CloseAll)(1.second).await
system.shutdown()
}
}
Then, the test spec:
class ElevationClientSpec extends Specification with Mockito{
val mockResponse = mock[HttpResponse]
val mockStatus = mock[StatusCode]
mockResponse.status returns mockStatus
mockStatus.isSuccess returns true
val json = """
{
"results" : [
{
"elevation" : 8815.71582031250,
"location" : {
"lat" : 27.9880560,
"lng" : 86.92527800000001
},
"resolution" : 152.7032318115234
}
],
"status" : "OK"
}
"""
val body = HttpEntity(ContentType.`application/json`, json.getBytes())
mockResponse.entity returns body
val client = new ElevationClient{
override def sendAndReceive = {
(req:HttpRequest) => Promise.successful(mockResponse).future
}
}
"A request to get an elevation" should{
"return an elevation result" in {
val fut = client.elavation
val el = Await.result(fut, Duration(2, TimeUnit.SECONDS))
val expected = GoogleApiResult("OK",List(Elevation(Location(27.988056,86.925278),8815.7158203125)))
el mustEqual expected
}
}
}
So my approach here was to first define an overridable function in the ElevationClient called sendAndReceive that just delegates to the spray sendReceive function. Then, in the test spec, I override that sendAndReceive function to return a function that returns a completed Future wrapping a mock HttpResponse. This is one approach for doing what you want to do. I hope this helps.
There's no need to introduce mocking in this case, as you can simply build a HttpResponse much more easily using the existing API:
val mockResponse = HttpResponse(StatusCodes.OK, HttpEntity(ContentTypes.`application/json`, json.getBytes))
(Sorry for posting this as another answer, but don't have enough karma to comment)

Play2 testing controller methods using Action composition

I want to test a controller action using Action composition.
Here's an example of the composed action and its test code.
The Secured trait:
trait Secured {
def username(request: RequestHeader) = request.session.get(Security.username)
def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Auth.login)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
The controller:
MyController extends Contrller with Secured {
def simple = Action { Ok("ok") }
def simpleWithauth = withAuth { implicit username => implicit request=> Ok("ok") }
}
The test code:
// This work fine
val result1 = controller.simple()(FakeRequest())
// This wont compile
val result2 = controller.simpleWithAuth()(FakeRequest())
The latter would require a Request[Action[AnyContent], AnyContent]
but FakeRequest returns a Request[AnyContent]
Any pointers on how to create a fake request of the appropriate type?
This is what I had to do to test secured action
def wrappedActionResult[A](wrapped: Action[(Action[A], A)], request: Request[A]): Result = wrapped.parser(request).run.await.get match {
case Left(errorResult) => errorResult
case Right((innerAction, _)) => innerAction(request)
}
and the test
running(app) {
val result = wrappedActionResult(FakeController().securedAction, invalidRequest)
status(result) must_== UNAUTHORIZED
contentAsString(result) must_== "must be authenticated"
}
That doesn't work with Play 2.1 though, types have been changed...
UPDATE:
in Play 2.1 it's even easier
I've added some sugar
implicit class ActionExecutor(action: EssentialAction) {
def process[A](request: Request[A]): Result = concurrent.Await.result(action(request).run, Duration(1, "sec"))
}
and the test now looks like this
running(app) {
val result = FakeController().securedAction process invalidRequest
status(result) must_== UNAUTHORIZED
contentAsString(result) must_== "must be authenticated"
}
UPDATE: here is a related blog post i wrote sometime ago
http://www.daodecode.com/blog/2013/03/08/testing-security-dot-authenticated-in-play-2-dot-0-and-2-dot-1/
Have you tried using the routeAndCall approach?
val Some(result) = routeAndCall(FakeRequest(POST, "/someRoute"))
You can add whatever parameters you need in your request with the asFormUrlEncodedBody method and/or adding things to the session with the withSession method.