Spock: Using ConfineMetaClassChanges when using MetaClass mocking
One of the handy features of Groovy is to change the behavior of classes using MOP (Meta-Object Protocol). We change the metaClass
property of a class to alter an implementation. You can for example override a static method of a class. See the example below:
class UserSpec extends Specification {
def “test user”() {
given:
// we can mock static methods...
User.metaClass.static.findByName = { name ->
new User(name: ‘Albert’) }
when:
User user = User.findByName(‘Albert’)
then:
user.name == ‘Albert’
when:
// .. but also non-static methods
user.metaClass.getName = { return ‘Dries’ }
then:
user.name == ‘Dries’
}
}
However this way of mocking has a nasty side effect. Since we apply changes on the User
on class level, the changes are also applied in all our other test cases. This can have some unexpected behavior in your other tests To prevent this Spock has introduced the @ConfineMetaClassChanges
annotation. This annotation makes sure the class will return to its original state when it was used in our test. See the example below:
@ConfineMetaClassChanges([User]) // you can add multiple classes in the annotation
class UserSpec extends Specification {
...
}
Be aware: this means when some other test did changes to the User
class without using @ConfineMetaClassChanges
, these changes will be still applicable when the test which has @ConfineMetaClassChanges
restores the User
class to its old state.