如何在grails中单元测试服务时模拟请求
问题描述:
我想单元测试具有需要请求对象的方法的服务。如何在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来单元测试我的类。
谢谢
答
一个简单的方法来模拟这些,是修改元类RequestContextHolder
返回一个模拟时getRequestAttributes()
被调用。
我写了一个简单的规范来做到这一点,当它不起作用时感到非常惊讶!所以这原来是一个相当有趣的问题。经过一番调查后,我发现在这个特殊情况下,有几个陷阱需要注意。
当您检索请求对象,
RCH.requestAttributes.request
,您是通过一个接口RequestAttributes
没有实现的getRequest()
方法这样做。如果返回的对象实际上具有这个属性,这在groovy中是完全正确的,但在嘲笑spock中的RequestAttributes
接口时不起作用。所以你需要模拟一个实际上有这种方法的接口或类。我第一次尝试解决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
}
}
答
此代码似乎为一个基本单元测试工作(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”开头......或者您可以用其他方式处理代码。