[groovy-user] metaclass: how to replace and restore a method?

150 views
Skip to first unread message

Aled Sage

unread,
Dec 2, 2011, 1:29:16 PM12/2/11
to us...@groovy.codehaus.org
Hi,

I'm having trouble with replacing and then restoring a method using
metaClass, to override behaviour for a test. Any help much appreciated.

In one of my tests, I inject:
Foo.metaClass.myBusinessLogic = { /* record stuff here */ }

That works well for intercepting the (expensive) call and recording it.
But everything in the test suite then subsequently fails because the
method is now a no-op! I've been trying to restore the original method
in the tearDown.

---
My attempts have involved code like that below. But for each of my
attempts, it keeps writing out "replacement".

class ReplacingAndRestoringMetaMethod {
public static void main(String[] args) {
def origMethod = Foo.metaClass.myBusinessLogic
//def origMethod2 =
Foo.metaClass.getMetaMethod("myBusinessLogic", new Object[0])

Foo.metaClass.myBusinessLogic = { println "replacement" }
Foo.metaClass.myBusinessLogic = origMethod

new Foo().myBusinessLogic()
}
}

public class Foo {
public void myBusinessLogic() { println "orig" }
}

Thanks, Aled

p.s. I'd prefer to use
http://docs.codehaus.org/display/GROOVY/Groovy+Mocks, but the code
hasn't been written to support such injection...


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


Paulo Gabriel Poiati

unread,
Dec 2, 2011, 2:03:54 PM12/2/11
to us...@groovy.codehaus.org
One way to do this is using categories:

class Foo {

    def bar() {
        'Original'
    }
    
}

class FooTestCategory {
    
    static String bar(Foo foo) {
        'Mocked'
    }
    
}

def foo = new Foo()

assert "Original" == foo.bar()

use (FooTestCategory) {
    assert "Mocked"== foo.bar()
}

assert "Original" == foo.bar()


[]'s
Paulo Poiati

blog.paulopoiati.com

Aled Sage

unread,
Dec 2, 2011, 5:31:56 PM12/2/11
to us...@groovy.codehaus.org
Thanks Paulo, that works great.

It was made a bit more complicated by my app-under-test being multi-threaded, so I had to inject a thread factory that wraps each Runnable.run in a `use (FooTestCategory)`. Luckily I could inject that thread factory using another test category :-)

Thanks again for the fast response,

Aled

Dinko Srkoc

unread,
Dec 2, 2011, 6:32:12 PM12/2/11
to us...@groovy.codehaus.org
On 2 December 2011 19:29, Aled Sage <aled...@cloudsoftcorp.com> wrote:
> Hi,
>
> I'm having trouble with replacing and then restoring a method using
> metaClass, to override behaviour for a test. Any help much appreciated.
>
> In one of my tests, I inject:
>        Foo.metaClass.myBusinessLogic = { /* record stuff here */ }
>
> That works well for intercepting the (expensive) call and recording it. But
> everything in the test suite then subsequently fails because the method is
> now a no-op! I've been trying to restore the original method in the
> tearDown.
>

In order to restore the original method you might try putting this in
the tearDown:

GroovySystem.metaClassRegistry.removeMetaClass Foo

This may have the same effect:

Foo.metaClass = null


Cheers,
Dinko

Coderextreme

unread,
Feb 18, 2012, 3:38:37 AM2/18/12
to us...@groovy.codehaus.org
It looks like you have to have an instance before using categories. What do
you do if you don't have an instance, for example, I am replacing the method
static method bar in class Foo:

Foo.metaClass.'static'.bar = { "Mocked" }

What's the best way restore bar to it's original state? Or is there a
better solution (I hope).

--
View this message in context: http://groovy.329449.n5.nabble.com/metaclass-how-to-replace-and-restore-a-method-tp5042834p5494930.html
Sent from the groovy - user mailing list archive at Nabble.com.

Coderextreme

unread,
Feb 18, 2012, 4:18:51 AM2/18/12
to us...@groovy.codehaus.org
This seems to work. I am not sure I like it though. Any better solutions?


class Foo {

static def bar() {
'Original'
}
}

assert "Original" == Foo.bar()

Foo.metaClass.'static'.bar = {"Mocked" }

assert "Mocked" == Foo.bar()

Foo.metaClass = null

assert "Original" == Foo.bar()

Foo.metaClass.'static'.bar = {"Mocked" }

assert "Mocked" == Foo.bar()

Foo.metaClass = null

assert "Original" == Foo.bar()


--
View this message in context: http://groovy.329449.n5.nabble.com/metaclass-how-to-replace-and-restore-a-method-tp5042834p5494972.html

Edward Sumerfield

unread,
Feb 18, 2012, 9:59:09 AM2/18/12
to us...@groovy.codehaus.org

Yes, that has worked for me too but if you have two methods, how would you revert just one to it's original version.

Ed

Coderextreme

unread,
Feb 23, 2012, 3:32:59 PM2/23/12
to us...@groovy.codehaus.org
Reply all
Reply to author
Forward
0 new messages