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

Dan*_* T. 27 grails serialization json grails-2.0

我有一个Grails服务,它通过执行HTTP调用使用第三方服务发送电子邮件:

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

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

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

但是,当我执行此单元测试时,它在尝试进行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
}
Run Code Online (Sandbox Code Playgroud)

当我尝试进行as JSON转换时,我得到了同样的异常.

有谁知道为什么我得到这个例外,我怎么能解决它?

Ste*_*sen 68

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

  • 这是唯一对我有用的答案 (5认同)

Rap*_*ael 9

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

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

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

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

def tearDown(){
    java.util.LinkedHashMap.metaClass.asType = null
}
Run Code Online (Sandbox Code Playgroud)

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

  • 很棒的答案 - 让我走向正确的方向.给定asType的语法不起作用,但`c.newInstance(delegate)`为我做了. (6认同)
  • 这不起作用,正确的解决方案是添加`@TestMixin(ControllerUnitTestMixin)`注释(查看@ Stephen的答案如下) (2认同)

ayZ*_*gen 7

由于 Grails 3.3.xgrails-test-mixins插件已弃用。@see迁移指南

对于这个问题,您应该实现GrailsWebUnitTest来自Grails 测试支持框架的内容

  • 谢谢,这让我为 Grails 3.3.x 指明了正确的方向。但您可能有兴趣知道我最终找到了[范围更窄的解决方案](/sf/answers/4262295621/)。 (2认同)

Dou*_*aul 6

我刚刚遇到这个问题,我真的不想GrailsWebUnitTest按照这里另一个答案中的建议来实现。我想让我的服务测试尽可能“纯粹”和精简。我最终这样做了:

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

void cleanupSpec() {
    ConvertersConfigurationHolder.clear()
}
Run Code Online (Sandbox Code Playgroud)

GrailsWebUnitTest这就是当您实施时(通过WebSetupSpecInterceptor和)在幕后发生的情况WebCleanupSpecInterceptor


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

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


小智 5

您可以在 setUp() 中初始化 JSON。有各种实现 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);
Run Code Online (Sandbox Code Playgroud)