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

Posted

技术标签:

【中文标题】如何在 Spock 控制器测试中模拟 Grails 请求对象方法【英文标题】:How to mock Grails request object method in Spock controller test 【发布时间】:2022-01-04 10:45:36 【问题描述】:

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

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

我尝试过的事情:

应该根据这篇文章工作(但不):How to mock HttpServletRequest in Spock

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

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

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

可重现的 repo(运行 ApplicationControllerSpec): https://github.com/Zorobay/test-app

【问题讨论】:

请编辑您的问题,提供MCVE,而不仅仅是迷你测试sn-p。可重复的测试用例极大地提高了您快速获得有用答案的机会。 @kriegaex 好了 :) 我现在可以重现这种情况,但我以前从未使用过 Grails 或其特殊的测试支持。我看到这个请求已经是某种特殊的模拟了。也许可以以某种方式配置该模拟,但您也注意到,没有直接设置阅读器的方法,这意味着只读。我想我在这里超出了我的深度,我不想开始考虑 Groovy 元类技巧。 Traits 也包含在 Grails 测试中,这并没有让事情变得更容易。也许另一种 Grails 测试类型更适合,我不知道。其他人可以更好地帮助你,对不起。 您可以使用全局 GroovySpy,因为 grails 中的所有内容都使用 Groovy。 @LeonardBrünings 我不确定我将如何完成这项工作。我试过这个,但它似乎没有效果(代码添加到问题描述中)。 【参考方案1】:

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

我发现可以在控制器中操作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")

【讨论】:

别忘了在 build.groovy 文件中需要这个:compile "org.grails:grails-test"

以上是关于如何在 Spock 控制器测试中模拟 Grails 请求对象方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spock 进行 Grails 测试 - 选择哪个模拟框架?

Grails Spock单元测试需要模拟事务管理器

Grails / Spock:如何在类中模拟从类本身调用方法的单个方法?

如何使用 Spock 测试 Grails 服务?

IntelliJ IDEA 的零覆盖:带有 Spock 单元测试的 Grails

在 Intellij Idea 中运行 grails 2.1.3 测试:Spock 测试中出现奇怪错误:无法添加域类 [class x.y.Z]。它不是域