2013-03-06 5 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(). Это прекрасно в хорошем состоянии, если возвращаемый объект действительно имеет это свойство, но не будет работать, когда высмеивает интерфейс 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, поскольку имя функции не начинается с «теста» ... или вы можете работать с кодом каким-либо другим способом.

Смежные вопросы