控制器单元测试中,领域对象的JSON渲染失败

3

我正在编写一个单元测试,测试一个将领域类呈现为JSON响应的Grails控制器:

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)是可行的:
```

将返回结果从领域类更改为地图(Map)是可行的:

```
render ([data: [id: domainInst.id]] as JSON)

为什么JSON marshaller在domain类上出现问题?它可以正常工作,但在模拟测试环境中却不能。有没有办法让这个测试工作?


请查看我的答案作为解决方法——我已经打开了一个拉取请求来在未来的版本中修复这个问题。 - Daniel Woods
1个回答

1

看起来你需要进行一些微调,让转换器意识到你正在尝试将一个域类渲染为JSON对象。当你手动将id放入map中时,它可以工作,因为它是从Map对象而不是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)
    }
}

希望这能有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接