Efectos de la manipulación de metaClass en Groovy / Grails

Groovy Logo

Haciendo unas pruebas unitarias en grails me encontré con un comportamiento de Groovy que desconocía.

Desde la versión de Groovy 1.6 se puede manipular el metaclass de una instancia (al menos fácilmente) además de hacerlo para toda la clase como es costumbre.

Para realizar un test, debía agregar por metaClass dos métodos, uno sobre la clase y otro sobre una instancia particular de dicha clase.

Misteriosamente para mi, al ejecutar el test, me decía que el método que agregaba a la clase no existía. Entonces escribí el siguiente caso de prueba para entender que era lo que estaba pasando

class MyTestClass {
}

def myTestInstance = new MyTestClass()

myTestInstance.metaClass.testInstanceMethod = { true }
MyTestClass.metaClass.testClassMethod = { true }

assert myTestInstance.testInstanceMethod() == true, "La instancia tiene el método declarado en el metaClass de la instancia"
assert myTestInstance.testClassMethod() == true, "La instancia tiene el método declarado en el metaClass de la clase"

A simple vista este test pareciera funcionar. Agrego a la instancia un método, luego le agrego a la clase otro método y luego testeo que esos métodos existan y que retornen true.

Para mi sorpresa, al ejecutar este script, el resultado fue

groovy.lang.MissingMethodException: No signature of method: MyTestClass.testClassMethod() is applicable for argument types: () values: []
Possible solutions: testClassMethod(), testClassMethod(java.lang.Object)

Investigando encontré que si invertía el orden de las líneas 06 y 07, que se encuentran resaltadas en el script anterior, se solucionaban todos los problemas y funcionaba como se esperaba el test.

¿Qué supongo que está sucediendo?

Cuando se modifica el metaclass de una instancia, se copia el estado actual del metaclass de la clase a la que dicha instancia pertenece y se modifica esta copia que queda como una copia privada de la instancia y sin relación con la metaclass de la partió.

Por lo tanto, luego, cuando se modifica el metaclass de la clase, agregándole un nuevo método, este cambio no se ve reflejado en las instancias que previamente fueron modificadas.

Moraleja: siempre modificar el metaclass de las clases primero y luego, cuando ya no quedan modificaciones a nivel clase, se pueden hacer las modificaciones a nivel instancia.


Discussion Area - Leave a Comment