在 Grails 中对服务进行单元测试时如何模拟请求
Posted
技术标签:
【中文标题】在 Grails 中对服务进行单元测试时如何模拟请求【英文标题】:How to mock a request when unit testing a service in grails 【发布时间】:2013-02-21 22:47:54 【问题描述】:我正在尝试对具有需要请求对象的方法的服务进行单元测试。
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 对我的课程进行单元测试。
谢谢
【问题讨论】:
【参考方案1】:此代码似乎适用于基本单元测试 (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”开头...或者您可以以其他方式处理代码。
【讨论】:
【参考方案2】:模拟这些的一种简单方法是修改RequestContextHolder
的元类,以便在调用getRequestAttributes()
时返回一个模拟。
我为此编写了一个简单的规范,但当它不起作用时,我感到非常惊讶!所以这是一个非常有趣的问题。经过一番调查,我发现在这种特殊情况下,有几个陷阱需要注意。
当您检索请求对象RCH.requestAttributes.request
时,您是通过未实现getRequest()
方法的接口RequestAttributes
执行此操作的。如果返回的对象实际上具有此属性,这在 groovy 中非常好,但在 spock 中模拟 RequestAttributes
接口时将不起作用。因此,您需要模拟一个实际具有此方法的接口或类。
我第一次尝试解决 1.,是将模拟类型更改为 ServletRequestAttributes
,它确实有一个 getRequest()
方法。然而,这个方法是最终的。当使用最终方法的值存根模拟时,存根值将被简单地忽略。在这种情况下,null
被返回。
通过为此测试创建一个名为MockRequestAttributes
的自定义接口,并将此接口用于规范中的 Mock,可以轻松克服这两个问题。
这导致了以下代码:
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
【讨论】:
以上是关于在 Grails 中对服务进行单元测试时如何模拟请求的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Grails 2.0 服务中对 i18n 注入 messageSource 的使用进行单元或集成测试