使用带有Spock单元测试的@PostConstruct的不可测试grails(2.5.4)服务



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

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

在为该服务编写单元测试时,我在斯波克遇到了一条死胡同。

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

这有一个反常的副作用:

我的init代码失败,要么是因为我未能添加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

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

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

最新更新