2013-03-06 15 views
7

Próbuję jednostce przetestować usługę, która ma metodę wymagającą obiektu żądania.Jak wyśmiać żądanie, gdy jednostka testuje usługę w 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 */ 
     } 
    } 
} 

W jaki sposób kpisz z obiektu żądania?

A przy okazji, używam Spocka do testowania moich zajęć.

Dziękuję

Odpowiedz

5

jeden prosty sposób na Mock nich jest modyfikacja meta klasy dla RequestContextHolder zwrócić makiety gdy getRequestAttributes() nazywa.

Napisałem do tego prostą specyfikację i byłem bardzo zaskoczony, kiedy to nie zadziałało! To okazało się dość interesującym problemem. Po pewnym dochodzeniu odkryłem, że w tym konkretnym przypadku jest kilka pułapek, o których należy pamiętać.

  1. Podczas pobierania obiektu żądania, RCH.requestAttributes.request, robisz to za pomocą interfejsu RequestAttributes że nie implementuje metodę getRequest(). Jest to idealnie w porządku, jeśli zwracany obiekt faktycznie ma tę właściwość, ale nie zadziała podczas kpienia z interfejsuw spocku. Będziesz więc musiał sfałszować interfejs lub klasę, która faktycznie ma tę metodę.

  2. Moja pierwsza próba rozwiązania 1. polegała na zmianie typu próbnego na ServletRequestAttributes, która ma metodę getRequest(). Ta metoda jest jednak ostateczna. Po odgadnięciu makiety z wartościami dla metody ostatecznej, wartości znikające są po prostu ignorowane. W tym przypadku zwrócono null.

Oba te problemy było łatwo przezwyciężyć, tworząc niestandardowy interfejs dla tego testu, zwany MockRequestAttributes i używać tego interfejsu do Mock w spec.

Spowodowało to w poniższym kodzie:

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

Kod ten wydaje się działać dla badanej jednostki podstawowe (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) 
} 

Może być dodany jako funkcja do standardowego testu Grails, ponieważ nazwa funkcji nie zaczyna się od "test" ... lub możesz pracować z kodem w inny sposób.

Powiązane problemy