ASSUNTO
Continuando e completando o assunto do card de 11 meses atrás Desfazer alterações no metaclass em testes de unidade (leia-o primeiro):
CONCLUSÕES
-
Teste que herda da classe GrailsUnitTestCase na implementação do Grails 2.2.4 não sofre com o problema de metaclass alterado em testes de unidade desde que seja utilizada super.tearDown() em sua implementação. Lembrando que caso você não sobrescreva o método void tearDown() este comportamento herdado já é executado por padrão. Perceba que o uso de registerMetaClass(Classe) (implementação exclusiva de GrailsUnitTestCase) de qualquer forma depende do super.tearDown() e não faz nenhum milagre sozinho, já o super.tearDown() sozinho faz.
-
Teste que herda da classe GMockTestCase na implementação do abandonado Gmock 0.8.3 já passa a sofrer com o problema de metaclass alterado em testes de unidade. A solução é a apresentada no card anterior, com o uso da seguinte chamada no final do próprio método de teste ou no tearDown da classe:
GroovySystem.metaClassRegistry.removeMetaClass(Classe)
-
Teste que herda da classe GroovyTestCase (que por sinal é a superclasse de GMockTestCase) também sofre com o problema de metaclass alterado em testes de unidade. A solução também é o uso do removeMetaClass no final do próprio método de teste ou no tearDown da classe.
-
Teste que herda da classe Specification/UnitSpec/IntegrationSpec na implementação do spock 0.7 também sofre com o problema de metaclass alterado em testes de unidade. A solução também é o uso do removeMetaClass, que aqui pode ser utilizada no próprio teste na seção cleanup, ou em um dos métodos cleanupSpec ou cleanup da classe, de acordo com o escopo necessário.
-
Teste que não herda nenhuma classe e usa a estrutura de anotações @TestFor/@Mock/@WithGMock, segundo o analisado, não sofre com o problema de metaclass alterado em testes de unidade de forma alguma. Aparentemente cada teste, seja na mesma classe ou não, é completamente independente quanto aos metaclass alterados. Na "desconfiança" seria indicado o uso da chamada removeMetaClass em (a) um método qualquer com a anotação @After ou (b) no método tearDown para evitar problemas futuros.
COMPROVAÇÕES
Crie as seguintes classes e mande executar os testes de todas elas de uma vez só.
GrailsUnitTestCase e GMockTestCase e GroovyTestCase
class ArquivoCausaBug1Tests extends GrailsUnitTestCase {
void tearDown() {
super.tearDown() //Necessário para impedir problemas
// processamento adicional qualquer ...
}
void testA() {
boolean replaceCalled = false
String.metaClass.replace = { CharSequence a, CharSequence b ->
replaceCalled = true
return 'metaclass usado'
}
assertEquals 'metaclass usado', 'aba'.replace('b', 'a')
assertEquals true, replaceCalled
}
void testB() {
assertEquals 'aaa', 'aba'.replace('b', 'a')
}
}
class ArquivoCausaBug2Tests extends GMockTestCase {
void tearDown() {
super.tearDown()
GroovySystem.metaClassRegistry.removeMetaClass(String) //Necessário para impedir problemas
}
void testA() {
boolean replaceCalled = false
String.metaClass.replace = { CharSequence a, CharSequence b ->
replaceCalled = true
return 'metaclass usado'
}
assertEquals 'metaclass usado', 'aba'.replace('b', 'a')
assertEquals true, replaceCalled
}
void testB() {
assertEquals 'aaa', 'aba'.replace('b', 'a')
}
}
class ArquivoCausaBug3Tests extends GroovyTestCase {
void tearDown() {
super.tearDown()
GroovySystem.metaClassRegistry.removeMetaClass(String) //Necessário para impedir problemas
}
void testA() {
boolean replaceCalled = false
String.metaClass.replace = { CharSequence a, CharSequence b ->
replaceCalled = true
return 'metaclass usado'
}
assertEquals 'metaclass usado', 'aba'.replace('b', 'a')
assertEquals true, replaceCalled
}
void testB() {
assertEquals 'aaa', 'aba'.replace('b', 'a')
}
}
class ArquivoIndependenteSofreBugTests extends GrailsUnitTestCase {
void testB() {
assertEquals 'aaa', 'aba'.replace('b', 'a')
}
}
Specification / UnitSpec / IntegrationSpec
class ArquivoCausaBugSpec extends UnitSpec {
void 'test A'() {
setup:
boolean replaceCalled = false
String.metaClass.replace = { CharSequence a, CharSequence b ->
replaceCalled = true
return 'metaclass usado'
}
expect:
'metaclass usado' == 'aba'.replace('b', 'a')
replaceCalled
cleanup:
GroovySystem.metaClassRegistry.removeMetaClass(String) //Necessário para impedir problemas
}
void 'test B'() {
expect:
'aaa' == 'aba'.replace('b', 'a')
}
}
class ArquivoIndependenteSofreBugSpec extends UnitSpec {
void 'test B'() {
expect:
'aaa' == 'aba'.replace('b', 'a')
}
}
Anotações
@TestFor(DomainClass)
@WithGMock
class ArquivoCausaBug3Tests {
void testA() {
assertEquals 'aaa', 'aba'.replace('b', 'a')
}
void testB() {
boolean replaceCalled = false
String.metaClass.replace = { CharSequence a, CharSequence b ->
replaceCalled = true
return 'metaclass usado'
}
assertEquals 'metaclass usado', 'aba'.replace('b', 'a')
assertEquals true, replaceCalled
}
void testC() {
assertEquals 'aaa', 'aba'.replace('b', 'a')
}
}
FONTES ORIGINAIS DE ESTUDO (além dos arquivos próprios criados)