Непроверяемая служба Grails (2.5.4) с использованием @PostConstruct с модульным тестированием Spock

У меня есть служба, которую я хочу инициализировать с помощью @PostConstuct, выбрав некоторые записи конфигурации в Config.groovy.

Я также хочу проверить правильность настройки этих записей и создать исключение, чтобы увидеть, что приложение было неправильно настроено.

При написании юнит-теста для этого сервиса я зашел в тупик в Spock.

Спок, по-видимому, вызывает метод @PostConstruct, но только в экземпляре Shared Service, а затем выполняет любые методы экземпляра, которые вы тестируете, в реальном тестируемом экземпляре.

Это имеет извращенный побочный эффект:

Мой код инициализации либо дает сбой, потому что я не могу добавить 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


person Luis Muñiz    schedule 30.07.2016    source источник


Ответы (1)


Мой код инициализации либо дает сбой, потому что я не могу добавить setupSpec для инициализации общего экземпляра, либо он терпит неудачу в тестируемом методе, потому что конфигурация фактически не была установлена ​​для этого экземпляра.

Мой совет - просто вызвать метод init, поскольку вы проверяете логику и функциональность метода, а не то, работает ли @PostConstruct, это кажется наиболее разумным.

person helgew    schedule 30.07.2016
comment
Правда, это приемлемо. Тестирование @PostConstuct — это аспект интеграции - person Luis Muñiz; 30.07.2016