如何在grails中单元测试服务时模拟请求

时间:2013-03-06 22:24:17

标签: unit-testing grails groovy spock

我正在尝试对具有需要请求对象的方法的服务进行单元测试。

import org.springframework.web.context.request.RequestContextHolder as RCH

class AddressService {

    def update (account, params) {
        try {
            def request = RCH.requestAttributes.request
            // retrieve some info from the request object such as the IP ...
            // Implement update logic
        } catch (all) { 
            /* do something with the exception */ 
        }
    }
}

如何模拟请求对象?

顺便说一句,我正在使用Spock对我的课程进行单元测试。

谢谢

2 个答案:

答案 0 :(得分:7)

模拟这些的一种简单方法是修改RequestContextHolder的元类,以便在调用getRequestAttributes()时返回模拟。

我为此做了一个简单的规范,当它不起作用时非常惊讶!所以这被证明是一个非常有趣的问题。经过一番调查,我发现在这种特殊情况下,有一些陷阱需要注意。

  1. 当您检索请求对象RCH.requestAttributes.request时,您通过未实现RequestAttributes方法的接口getRequest()进行检索。如果返回的对象实际上具有此属性,则在groovy中完全正常,但在spock中模拟RequestAttributes接口时将无法工作。因此,您需要模拟实际具有此方法的接口或类。

  2. 我第一次尝试解决1.是将模拟类型更改为ServletRequestAttributes,它有一个getRequest()方法。但是,这种方法是最终的。当使用最终方法的值来存储模拟时,简单地忽略存根值。在这种情况下,返回了null

  3. 通过为此测试创建一个名为MockRequestAttributes的自定义界面,可以轻松克服这些问题,并在规范中将此接口用于模拟。

    这导致了以下代码:

    import org.springframework.web.context.request.RequestContextHolder
    
    // modified for testing
    class AddressService {
    
        def localAddress
        def contentType
    
        def update() {
            def request = RequestContextHolder.requestAttributes.request
            localAddress = request.localAddr
            contentType = request.contentType
        }
    }
    

    import org.springframework.web.context.request.RequestAttributes
    import javax.servlet.http.HttpServletRequest
    
    interface MockRequestAttributes extends RequestAttributes {
        HttpServletRequest getRequest()
    }
    

    import org.springframework.web.context.request.RequestContextHolder
    import spock.lang.Specification
    
    import javax.servlet.http.HttpServletRequest
    
    class MockRequestSpec extends Specification {
    
        def "let's mock a request"() {
            setup:
            def requestAttributesMock = Mock(MockRequestAttributes)
            def requestMock = Mock(HttpServletRequest)
            RequestContextHolder.metaClass.'static'.getRequestAttributes = {->
                requestAttributesMock
            }
    
            when:
            def service = new AddressService()
            def result = service.update()
    
            then:
            1 * requestAttributesMock.getRequest() >> requestMock
            1 * requestMock.localAddr >> '127.0.0.1'
            1 * requestMock.contentType >> 'text/plain'
            service.localAddress == '127.0.0.1'
            service.contentType == 'text/plain'
    
            cleanup:
            RequestContextHolder.metaClass = null
        }
    
    }
    

答案 1 :(得分:5)

此代码似乎适用于基本单元测试(modified from Robert Fletcher's post here):

void createRequestContextHolder() {
    MockHttpServletRequest request = new MockHttpServletRequest()
    request.characterEncoding = 'UTF-8'

    GrailsWebRequest webRequest = new GrailsWebRequest(request, new MockHttpServletResponse(), ServletContextHolder.servletContext)
    request.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)

    RequestContextHolder.setRequestAttributes(webRequest)
}

它可以作为函数添加到您的标准Grails单元测试中,因为函数名称不是以“test”开头...或者您可以用其他方式处理代码。