Spock has the extension ConfineMetaClassChanges
that can be used to encapsulate meta class changes to a feature method or specification. We must apply the annotation @ConfineMetaClassChanges
to a feature method or to a whole specification to use this extension. Spock replaces the original meta class with a new one before a feature method is executed. After execution of a feature method the original meta class is used again. We could this by hand using the setup
, setupSpec
and their counter parts cleanup
and cleanupSpec
, but using this extension is so much easier. We must specify the class or classes whose meta class changes need to be confined as the value for the annotation.
In the following example we add a new method asPirate
to the String
class. We apply the @ConfineMetaClassChanges
to a method. This means the new method is only available inside the feature method.
package com.mrhaki.spock import spock.lang.Specification import spock.lang.Stepwise import spock.util.mop.ConfineMetaClassChanges // We use @Stepwise in this specification // to show that changes in the metaClass // done in the first feature method do not // work in the second feature method. @Stepwise class PirateTalkSpec extends Specification { // After this feature method is finished, // the metaClass changes to the given // class (String in our case) are reverted. @ConfineMetaClassChanges([String]) def "talk like a pirate"() { setup: String.metaClass.asPirate = { -> return "Yo-ho-ho, ${delegate}" } expect: 'mrhaki'.asPirate() == 'Yo-ho-ho, mrhaki' } // In this feature method we no longer // can use the asPirate() method that was // added to the metaClass. def "keep on talking like a pirate"() { when: 'hubert'.asPirate() then: thrown(MissingMethodException) } }
In the following example code we apply the @ConfineMetaClassChanges
to the whole class. Now we see that the new method asPirate
is still available in another feature method, than the one that defined it.
package com.mrhaki.spock import spock.lang.Specification import spock.lang.Stepwise import spock.util.mop.ConfineMetaClassChanges // We use @Stepwise in this specification // to show that changes in the metaClass // done in the first feature method still // work in the second. @Stepwise // If set a class level then the // changes done to the metaClass of // the given class (String in our // example) are reverted after the // specification is finished. @ConfineMetaClassChanges([String]) class PirateTalkSpec extends Specification { def "talk like a pirate"() { setup: String.metaClass.asPirate = { -> return "Yo-ho-ho, ${delegate}" } expect: 'mrhaki'.asPirate() == 'Yo-ho-ho, mrhaki' } def "keep on talking like a pirate"() { expect: 'hubert'.asPirate() == 'Yo-ho-ho, hubert' } }
This post is very much inspired by this blog post of my colleague Albert van Veen.
Written with Spock 1.0-groovy-2.4.