如何对使用转换器的Grails服务进行单元测试?

时间:2012-08-02 21:00:49

标签: json grails serialization grails-2.0

我有一个Grails服务,可以通过HTTP呼叫使用第三方服务发送电子邮件:

class EmailService {
    def sendEmail(values) {
        def valueJson = values as JSON
        ... // does HTTP call to 3rd party service
    }
}

我编写了一个单元测试来测试这个服务(因为集成测试会旋转Hibernate和整个域框架,我不需要):

@TestFor(EmailService)
class EmailServiceTests {
    void testEmailServiceWorks() {
        def values = [test: 'test', test2: 'test2']
        service.sendEmail(values)
    }
}

但是,当我执行此单元测试时,它在尝试执行as JSON转换时失败并出现此异常:

  

org.apache.commons.lang.UnhandledException:org.codehaus.groovy.grails.web.converters.exceptions.ConverterException:类的Unconvertable对象:java.util.LinkedHashMap

然后我重新编写单元测试以执行以下操作:

void testEmailServiceWorks() {
    def value = [test: 'test', test2: 'test2']
    def valueJson = value as JSON
}

我尝试进行as JSON转换时遇到同样的异常。

有谁知道我为什么会遇到这个异常,以及如何解决这个问题?

6 个答案:

答案 0 :(得分:68)

即使您正在测试服务,也可以将@TestMixin(ControllerUnitTestMixin)注释应用于测试类,以使Grails设置JSON转换器。

答案 1 :(得分:9)

在域框架旋转时创建了JSON魔术。

您必须将测试更改为集成测试或模拟asType。

def setUp(){
    java.util.LinkedHashMap.metaClass.asType = { Class c ->
        new grails.converters."$c"(delegate)
    }
}

Rember要在tearDown中自行清理,你不会想要在你的测试套件中进行元编程泄漏。

def tearDown(){
    java.util.LinkedHashMap.metaClass.asType = null
}

编辑: 如果您来自未来,请考虑以下答案:https://stackoverflow.com/a/15485593/194932

答案 2 :(得分:4)

您可以在setUp()中初始化JSON。有各种marshallers实现ObjectMarshaller,需要将其添加到ConverterConfiguration以进行JSON转换才能工作。

http://grails.github.io/grails-doc/2.4.4/api/index.html?org/codehaus/groovy/grails/web/converters/marshaller/json/package-summary.html

示例:

 DefaultConverterConfiguration<JSON> defaultConverterConfig = new  DefaultConverterConfiguration<JSON>()
 defaultConverterConfig.registerObjectMarshaller(new CollectionMarshaller())
 defaultConverterConfig.registerObjectMarshaller(new MapMarshaller())
 defaultConverterConfig.registerObjectMarshaller(new GenericJavaBeanMarshaller())

 ConvertersConfigurationHolder.setTheadLocalConverterConfiguration(JSON.class, defaultConverterConfig);

答案 3 :(得分:2)

不推荐使用Grails 3.3.x grails-test-mixins插件。 @see migration guide

对于这个问题,您应该实现GrailsWebUnitTest,它来自Grails Testing Support Framework

答案 4 :(得分:1)

当我尝试对一个调用“将myMap渲染为JSON”的控制器进行单元测试时,我遇到了同样的错误。我们使用Grails 1.3.7并且在没有引入其他问题的情况下,没有其他解决方案适用于我。目前升级Grails不是我们的替代选择。

我的解决方案是使用JSONBuilder而不是“as JSON”,如下所示:

render(contentType: "application/json", {myMap})

请参阅http://docs.grails.org/latest/guide/theWebLayer.html#moreOnJSONBuilder

(我意识到这是旧的,但是来到这里寻找解决方案,其他人也可以这样做)

答案 5 :(得分:1)

我只是碰到了这个问题,我真的不想按照此处另一个答案的建议实施GrailsWebUnitTest。我想让我的服务测试保持“纯净”和“精益”。我最终这样做:

void setupSpec() {
    defineBeans(new ConvertersGrailsPlugin())
}

void cleanupSpec() {
    ConvertersConfigurationHolder.clear()
}

这是实现GrailsWebUnitTest(通过WebSetupSpecInterceptorWebCleanupSpecInterceptor)时在后台发生的情况。


也就是说,转换器似乎旨在用于Web层,主要是为了使从控制器透明地以不同格式透明地返回数据变得容易。值得考虑的是,为什么要测试的服务首先需要转换器。

例如,在我的情况下,有人使用JSON转换器将某些数据序列化为字符串,以便可以将其存储在数据库的单个字段中。这似乎不是转换器的合适用户,因此我计划更改其工作方式。在我的服务测试中使用转换器是一个临时解决方案,可以让我在重构之前提高测试覆盖率。