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
Richtig, das ist akzeptabel. Das Testen von @PostConstuct ist ein Integrationsaspekt – loteq