如何在 Spock 控制器测试中模拟 Grails 请求对象方法

How to mock Grails request object method in Spock controller test

我正在尝试为我的控制器之一编写测试 类。在这个控制器中,我调用 request.reader.text 如果正文包含非 utf-8 字符,它会抛出 MalformedInputException

这就是我在 Spock 测试中尝试测试和模拟的情况。最简单的方法是模拟 getReader() 方法,但事实证明这很困难。

我尝试过的事情:

应该根据这个post工作(但不是):

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

也试过这个,根据@LeonardBrünings 的评论(但似乎没有效果):

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

可复制回购 (运行 ApplicationControllerSpec): https://github.com/Zorobay/test-app

经过更疯狂的谷歌搜索,我终于设法找到了解决方案。虽然,这不像我希望的那样干净,但它有效!

我发现可以在控制器中操作 requestresponse 对象的唯一方法是使用新的 GrailsWebRequest 调用 RequestContextHolder.setRequestAttributes()。这样做的缺点是响应对象也必须被覆盖。然而,这不是一个大问题,因为它是在调用 render() 时“就地”操作的,所以我们可以只检查新创建的对象上的“将是”响应状态。我的 Spock 测试现在看起来像这样:

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}")
}