2016-07-30 38 views
3

Ich habe einen Dienst, den ich mit @PostConstuct initialisieren möchte, durch Abrufen einiger Konfigurationseinträge in Config.groovy.Untestable Grails (2.5.4) Service mit @PostConstruct mit Spock-Unit-Test

Ich möchte auch überprüfen, dass diese Einträge richtig konfiguriert waren, und eine Ausnahme auslösen, so dass ich sehe, dass die Anwendung falsch konfiguriert wurde.

Beim Schreiben eines Komponententests für diesen Dienst kam ich in Spock zu einer Sackgasse.

Spock ruft anscheinend die -Methode auf, jedoch nur auf der Instanz des gemeinsamen Dienstes, und führt dann alle von Ihnen getesteten Instanzmethoden für die tatsächlich getestete Instanz aus.

Dies hat einen perversen Nebeneffekt:

Mein init Code entweder versagt, weil ich ein setupSpec hinzufügen nicht die gemeinsame Instanz zu initialisieren, oder es scheitert bei dem Verfahren unter Test, da die Konfiguration tatsächlich nicht gesetzt wurde auf dieser Instanz.

Hier ist mein Service:

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()) 
    } 
} 

Hier ist mein erster Test:

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 
    } 
} 

Hier ist der zweite Test:

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 
    } 
} 

Gibt es eine Möglichkeit, diese sauber zu tun? Oder muss ich meinen Komponententest loslassen und einfach einen (langsameren) Integrationstest machen, um mich von dieser Verrücktheit zu lösen?

Sie können meine volle Grails App hier sehen:

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

Antwort

2

Mein init Code entweder versagt, weil ich eine setupSpec hinzufügen nicht die gemeinsame Instanz zu initialisieren, oder es scheitert bei dem Verfahren im Test , weil die Konfiguration für diese Instanz nicht festgelegt wurde.

Mein Rat ist, einfach die init-Methode aufrufen, da Sie die Logik und Funktionalität des Verfahrens testen und nicht, ob @PostConstruct funktioniert, scheint dies am sinnvollsten zu machen.

+0

Richtig, das ist akzeptabel. Das Testen von @PostConstuct ist ein Integrationsaspekt – loteq