Mock HTTPBuilder and POST requests in Grails

For some reason, mocking HTTPBuilder become a larger task than initially anticipated. In this post, the details on how to assert params, payload of the HTTPBuilder request as well as success or failure of the response is covered to have a good test

Here is an example of the HTTP POST request:

class NodeDriverProxyService {
   def http = new HTTPBuilder('http://localhost:8080')

    public registerUpdate(Long id, Long version){
        try{
            http.request(Method.POST, JSON){ req ->
                body = [
                        version: version,
                        id: id
                ]

                response.success = {resp, json ->
                    log.warn "cached object id: $id and version: $version; status code: " + resp.statusLine.statusCode
                }
            }
        }catch(Exception e){
            log.warn('Failure Initiate Connection with Node Driver: ' + e.message);
        }
    }
}

Here the httpBuild is initiated in line 2. The request takes ‘id’ and ‘version’. On the response, the message is logged with the response status(line 13). The error message is also logged if exception encountered at any time during request

1. Test Params

Here is the test for ensuring params(method, body, etc.) passed in the request accordingly

@Shared  id = 2, ver = 3
def "ensure params passed in correctly"(){
        setup:
        def httpBuildMock = new MockFor(HTTPBuilder.class)
        def reqPar = []
        def success

        def requestDelegate = [
                response: [:]
        ]

        httpBuildMock.demand.request(1){
            Method met, ContentType type, Closure b ->
            b.delegate = requestDelegate
            b.call()
            reqPar << [method: met, type: type, id: b.body.id, ver: b.body.version ]
        }

        when:
        httpBuildMock.use{
            service.registerUpdate(id,ver)
        }

        then:
        assert reqPar[0].method == Method.POST
        assert reqPar[0].type == ContentType.JSON
        assert reqPar.ver[0] == ver
        assert reqPar.id[0] == id
    }

First, we declare a list – reqPar(line 5) to hold all of the params since its accessible only within the request scope and we prefer to do assertions outside the request scope part of the ‘then’ block(line 23 – 27)

Next, we mock interface(line 8-10) of the RequestConfigDelegate class because it is the owner scope calling the request method. We also assign it in line 13 to the ‘request’ closure before executing it in next line

We need to execute the ‘request’ closure in line 15, because within that scope the body params are assigned, thus, available for us to access that we are doing in line 15. Beside body params, there may also uri.path, uri.query but in our implementation there isn’t.

2. Test Response

Our other test is going to be testing the successful and failure response.

@Shared  id = 2, ver = 3, status_code = 200, msg = 'test message'
 def "ensure response is handled accordingly for success and failure states"(){

        setup:
        def logTo = []
        def success
        def httpBuildMock = new MockFor(HTTPBuilder.class)

        def logService = [
                warn: { String message ->
                    logTo << [warn: message]
                }         
             ] as org.apache.commons.logging.Log
       
        def requestDelegate = [
                            response: [ 
                                 'statusLine': [ 'protocol': 'HTTP/1.1',
                                                 'statusCode': status_code, 
                                                 'status':'OK'
                                               ]
                                 ],
        ]
      
        httpBuildMock.demand.request(1){Method met, ContentType type, Closure b ->
            b.delegate = requestDelegate
            b.call()
            if(success) {
                requestDelegate.response.success(requestDelegate.response,[:])
            }else{
                throw new Exception(msg)
            }
        }
        service.log = logService

        httpBuildMock.use{
            success = state
            service.registerUpdate(id,ver)
        }

        expect:
        assert logTo[0].warn == message

        where:
        state   | message
        false   | 'Failure Initiate Connection with Node Driver: ' + msg
        true    | 'cached object id: ' + id + ' and version: ' + ver + '; status code: ' + status_code

    }

In our case, in both situation for successful and failure response there are messages logged, so we mock the log service as well in the lines 8-12.

Besides mocking the Log service and asserting the messages, we initiate the response in the highlighted line as it would be when our Http POST request would receive the response. For response, the same RequestConfigDelegate interface is also passed into as response, however. We add ‘statusLine’ in this mocked requestConfigDelegate instance since the ‘statusLine’ is being called and we also need it for the assertion in the ‘where’ block

Useful Links:

  • http://cscarioni.blogspot.com/2011/02/is-it-harder-to-unit-test-in-grails.html
  • http://groovy.codehaus.org/Developer+Testing+using+Closures+instead+of+Mocks
  • http://groovy.codehaus.org/Groovy+way+to+implement+interfaces
  • http://stackoverflow.com/questions/13790246/how-to-use-mockfor-to-mock-client-on-httpbuilder
  • http://groovyconsole.appspot.com/script/760001
  • http://jira.grails.org/browse/GRAILS-8617
  • http://groovy.codehaus.org/modules/http-builder/apidocs/groovyx/net/http/HTTPBuilder.html#request(groovyx.net.http.Method, groovy.lang.Closure)

Leave a Reply

Your email address will not be published. Required fields are marked *