Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a request when unit testing a service in grails

I am trying to unit test a service that has a method requiring a request object.

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 */ 
        }
    }
}

How do you mock the request object ?

And by the way, I am using Spock to unit test my classes.

Thank you

like image 605
ontk Avatar asked Mar 06 '13 22:03

ontk


People also ask

Which objects or methods do you mock in a unit test?

Mocking for unit testing is when you create an object that implements the behavior of a real subsystem in controlled ways. In short, mocks are used as a replacement for a dependency.

Why use mocks in unit testing?

Advanced: Mocking in Unit Test Mocking is useful to replace operations that should not be run in a testing environment, for instance, to replace operations that connect to a database and loads data when the testing environment does not have the same data access.


2 Answers

This code seems to work for a basic unit test (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)
}

It can be added as a function to your standard Grails unit test since the function name does not start with "test"... or you can work the code in some other way.

like image 120
EpicVoyage Avatar answered Sep 21 '22 10:09

EpicVoyage


One simple way to mock these, is to modify the meta class for RequestContextHolder to return a mock when getRequestAttributes() is called.

I wrote up a simple spec for doing this, and was quite surprised when it didn't work! So this turned out to be a quite interesting problem. After some investigation, I found that in this particular case, there are a couple of pitfalls to be aware of.

  1. When you retrieve the request object, RCH.requestAttributes.request, you are doing so via an interface RequestAttributes that does not implement the getRequest() method. This is perfectly fine in groovy if the returned object actually has this property, but won't work when mocking the RequestAttributes interface in spock. So you'll need to mock an interface or a class that actually has this method.

  2. My first attempt at solving 1., was to change the mock type to ServletRequestAttributes, which does have a getRequest() method. However, this method is final. When stubbing a mock with values for a final method, the stubbed values are simply ignored. In this case, null was returned.

Both these problems was easily overcome by creating a custom interface for this test, called MockRequestAttributes, and use this interface for the Mock in the spec.

This resulted in the following code:

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
    }

}
like image 23
Steinar Avatar answered Sep 18 '22 10:09

Steinar