我正在为Grails控制器编写单元测试,该控制器将域类呈现为JSON响应:
class MyController {
def find = {
def domainInst = MyDomainClass.get(params.id)
render ([data: domainInst] as JSON)
}
}
单元测试扩展了ControllerUnitTestCase,并为域对象提供了一个模拟:
class MyControllerTests extends ControllerUnitTestCase {
@Before
void setUp() {
super.setUp()
mockDomain(MyDomainClass, [new MyDomainClass(id: 7)])
}
@Test
void testFind() {
def inst = MyDomainClass.get(7)
controller.params.id = inst.id
controller.find()
assert(controller.response.json.data.id == inst.id)
}
除了JSON渲染之外,这一切看起来都很好用,它会散发出令人讨厌的堆栈痕迹:
| Failure: testFind(MyControllerTests)
| org.apache.commons.lang.UnhandledException:
org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class MyDomainClass
Caused by: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class MyDomainClass
at grails.converters.JSON.value(JSON.java:199)
at grails.converters.JSON.convertAnother(JSON.java:162)
at grails.converters.JSON.value(JSON.java:199)
at grails.converters.JSON.render(JSON.java:134)
... 5 more
Caused by: java.lang.reflect.InvocationTargetException
... 9 more
Caused by: groovy.lang.MissingMethodException: No signature of method: MyDomainClass.isAttached() is applicable for argument types: () values: []
Possible solutions: isAttached(), attach()
... 9 more
将返回值更改为Map而不是域类:
render ([data: [id: domainInst.id]] as JSON)
导致JSON编组程序死于域类的原因是什么?它适用于正常环境,但不适用于模拟测试环境。有没有办法让这个测试工作?
答案 0 :(得分:1)
看起来您可能需要进行一些微调,以使转换器意识到您正在尝试将域类呈现为JSON对象。当你手动将你的id放入一个地图时它会起作用,因为它是从一个Map对象而不是Grails域类渲染响应,而Grails域类需要经过一个特殊的ObjectMarshaller。
这样的事情:
// Domain Class
class Foo {
String foo
}
// Controller class
class MyController {
def find = {
def domainInst = Foo.get(params.id)
render domainInst as JSON
}
}
// Controller Test Class
class MyControllerTests extends ControllerUnitTestCase {
static application
@Before
void setUp() {
super.setUp()
// Register some common classes so that they can be converted to XML, JSON, etc.
def convertersInit = new ConvertersConfigurationInitializer()
convertersInit.initialize(application)
[ List, Set, Map, Errors ].each { addConverters(it) }
def xmlErrorMarshaller = new ValidationErrorsMarshaller()
XML.registerObjectMarshaller(xmlErrorMarshaller)
def jsonErrorMarshaller = new ValidationErrorsMarshaller()
JSON.registerObjectMarshaller(jsonErrorMarshaller)
ApplicationHolder.application.addArtefact("Domain", Foo)
mockDomain(Foo, [new Foo(foo: "foo")] )
}
@Test
void testJSON() {
def inst = Foo.list()[0]
controller.params.id = inst.id
def model = controller.find()
assert controller.response.json.foo == "foo"
}
@Override
protected def bindMockWebRequest(GrailsMockHttpServletRequest mockRequest, GrailsMockHttpServletResponse mockResponse) {
MockApplicationContext ctx = new MockApplicationContext()
application = new DefaultGrailsApplication([testClass] as Class[], getClass().classLoader)
application.initialise()
ctx.registerMockBean("grailsApplication", application)
ctx.registerMockBean(testClass.name, testClass.newInstance())
def lookup = new TagLibraryLookup(applicationContext: ctx, grailsApplication: application)
lookup.afterPropertiesSet()
ctx.registerMockBean("gspTagLibraryLookup", lookup)
ctx.registerMockBean(GroovyPagesUriService.BEAN_ID, new DefaultGroovyPagesUriService())
mockRequest.servletContext.setAttribute(ApplicationAttributes.APPLICATION_CONTEXT, ctx)
mockRequest.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx)
webRequest = new GrailsWebRequest(mockRequest, mockResponse, mockRequest.servletContext)
mockRequest.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
RequestContextHolder.setRequestAttributes(webRequest)
}
}
希望这有帮助!