【问题标题】:Untestable grails (2.5.4) service using @PostConstruct with Spock unit testing使用带有 Spock 单元测试的 @PostConstruct 的不可测试的 grails (2.5.4) 服务
【发布时间】:2016-07-30 16:27:01
【问题描述】:

我希望通过在Config.groovy 中获取一些配置条目来使用@PostConstuct 初始化一个服务。

我还希望检查这些条目是否正确配置,并抛出异常以便我看到应用程序配置错误。

在为此服务编写单元测试时,我在 Spock 中走到了死胡同。

Spock 显然调用了@PostConstruct 方法,但仅在共享服务实例上调用,然后在实际测试实例上执行您测试的任何实例方法。

这有一个反常的副作用:

我的初始化代码要么失败,因为我未能添加 setupSpec 来初始化共享实例,要么在被测方法中失败,因为该实例上实际上没有设置配置。

这是我的服务:

package issue

import org.codehaus.groovy.grails.commons.GrailsApplication

import javax.annotation.PostConstruct

class MyService {
    GrailsApplication grailsApplication
    String property

    @PostConstruct
    void init() {
        println "Initializing... ${this}"
        property = grailsApplication.config.myProperty

//Enabling this business sanity check make the service untestable under Spock, because to be able to run, we need to initialize the configuration
// of the shared instance - PostConstruct is only called on the shared instance for some reason.
// But the execution of the method under test will not have the initialized property, because the service being executed is not the shared instance
        if (property == "[:]") {
            throw new RuntimeException("This property cannot be empty")
        }
    }


    void doSomething() {
        println "Executing... ${this}"
        println(property.toLowerCase())
    }
}

这是我的第一个测试:

package issue

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(MyService)
class MyServiceSpec extends Specification {

    def setup() {
        grailsApplication.config.myProperty = 'myValue'
    }

    void "It fails to initialize the service"() {
        expect:
        false // this is never executed
    }
}

这是第二个测试:

package issue

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(MyService)
class MyServiceWithSharedInstanceInitializationSpec extends Specification {

    //Initializing the shared instance grailsApplication lets the @PostConstruct work, but will fail during method test
    //because the instance that was initialized is the shared instance
    def setupSpec() {
        grailsApplication.config.myProperty = 'myValue'
    }

    void "It fails to execute doSomething"() {
        when:
        service.doSomething()

        then:
        def e = thrown(NullPointerException)
        e.message == 'Cannot invoke method toLowerCase() on null object'
        service.property == null
    }
}

有没有办法干净地做到这一点?还是我必须放弃我的单元测试而只做一个(较慢的)集成测试,以避开这种怪异现象?

您可以在此处查看我的完整 grails 应用程序:

https://github.com/LuisMuniz/grails-spock-issue-with-postconstruct

【问题讨论】:

    标签: unit-testing grails groovy grails-2.0 spock


    【解决方案1】:

    我的 init 代码要么失败,因为我未能添加 setupSpec 来初始化共享实例,要么在被测方法中失败,因为该实例上实际上没有设置配置。

    我的建议是简单地调用 init 方法,因为您正在测试该方法的逻辑和功能,而不是 @PostConstruct 是否有效,这似乎最有意义。

    【讨论】:

    • 没错,这是可以接受的。测试@PostConstruct 是一个集成方面
    猜你喜欢
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 2013-09-09
    • 2015-10-12
    • 2014-06-21
    • 2016-06-17
    相关资源
    最近更新 更多