2013-03-06 134 views
7

我想单元测试具有需要请求对象的方法的服务。如何在grails中单元测试服务时模拟请求

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来单元测试我的类。

谢谢

回答

5

一个简单的方法来模拟这些,是修改元类RequestContextHolder返回一个模拟时getRequestAttributes()被调用。

我写了一个简单的规范来做到这一点,当它不起作用时感到非常惊讶!所以这原来是一个相当有趣的问题。经过一番调查后,我发现在这个特殊情况下,有几个陷阱需要注意。

  1. 当您检索请求对象,RCH.requestAttributes.request,您是通过一个接口RequestAttributes没有实现的getRequest()方法这样做。如果返回的对象实际上具有这个属性,这在groovy中是完全正确的,但在嘲笑spock中的RequestAttributes接口时不起作用。所以你需要模拟一个实际上有这种方法的接口或类。

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

这两个问题很容易被创建此测试的定制界面,称为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 
    } 

} 
3

此代码似乎为一个基本单元测试工作(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”开头......或者您可以用其他方式处理代码。

相关问题