Search code examples
grailsgroovycontrollerspock

How to mock Grails request object method in Spock controller test


I'm trying to write a test for one of my controller classes. In this controller, I call request.reader.text which can throw MalformedInputException if the body contains non-utf-8 characters.

This is the case I'm trying to test and mock in my Spock test. The easiest would be if I could mock the getReader() method, but that turns out to be difficult.

Things I've tried:

Should work according to this post (but does not): How to mock HttpServletRequest in Spock

GrailsMockHttpServletRequest request = new GrailsMockHttpServletRequest()
request.getReader() >> {
    throw new MalformedInputException(1)
}

Also tried this, as per @LeonardBrünings comment (but it seems to have no effect):

GroovySpy(GrailsMockHttpServletRequest, global: true) {
    getReader() >> {
        throw new MalformedInputException(1)
    }
}

Reproduceable repo (run ApplicationControllerSpec): https://github.com/Zorobay/test-app


Solution

  • I finally managed to find a solution after some more frantic googling. Although, this is not as clean as I would have wished, it works!

    The only way I've found it possible to manipulate the request and response objects in a controller is by calling RequestContextHolder.setRequestAttributes() with a new GrailsWebRequest. The downside to this is that the response object also has to be overwritten. This is not a big problem however, as it is manipulated "in-place" when calling render(), so we can just check the "would be" response status on our newly created object. My Spock test now looks like this:

    def "validerHtml: håndterer MalformedInputException"() {
        given:
            String charset = "UTF-16"
            GrailsMockHttpServletRequest mockRequest = Spy(GrailsMockHttpServletRequest)
            mockRequest.getReader() >> {
                throw new MalformedInputException(1)
            }
            mockRequest.setContentType("text/html;charset=${charset}")
    
            GrailsMockHttpServletResponse mockResponse = new GrailsMockHttpServletResponse()
            GrailsWebRequest webRequest = new GrailsWebRequest(mockRequest, mockResponse, mockRequest.getServletContext())
            mockRequest.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
            RequestContextHolder.setRequestAttributes(webRequest)  // Here we overwrite the web request
        when:
            controller.validateHtml()
        then:
            0 * controller.myService.validateMessage(*_)
            // Have to check would-be response on our mocked response
            mockResponse.status == HttpStatus.BAD_REQUEST.value()
            mockResponse.text.startsWith("Could not read request body using charset: ${charset}")
    }