9

I have the following in the test setup:

    def originalPostAsXml = RestClient.&postAsXml

    RestClient.metaClass.'static'.postAsXml = {
        String uriPath, String xml ->
        return 65536
    }

and in the test cleanup:

    RestClient.metaClass.'static'.postAsXml = originalPostAsXml

But when the next test runs, when it tries to execute RestClient.postAsXml, it runs into a StackOverflowError:

at groovy.lang.Closure.call(Closure.java:282)

It looks like RestClient.postAsXml recursively points to itself. What's the right way to reset a mocked-out static method?

Noel Yap
  • 18,822
  • 21
  • 92
  • 144
  • Here's a previous similar question: http://stackoverflow.com/questions/920582/how-to-change-behaviour-of-the-methed-in-groovy-using-that-method-in-metaclass – schmolly159 Dec 01 '11 at 22:25
  • You can reset the metaclass - See this question which has answers http://stackoverflow.com/questions/1612569/how-do-i-undo-meta-class-changes-after-executing-groovyshell – Sudhir N Feb 12 '16 at 14:27

3 Answers3

6

In a unit test, I often set the metaclass to null in the tearDown() which seems to allow the class to work as it did originally without my modifications.

example:

void setUp() {
    super.setUp()
    ServerInstanceSettings.metaClass.'static'.list = {
        def settings = [someSetting:'myOverride'] as ServerInstanceSettings
        return [settings]
    }
}

void tearDown() {
    super.tearDown()
    ServerInstanceSettings.metaClass.'static'.list = null
}

If you are using JUnit4 you can use @AfterClass instead in this case which makes more sense perhaps.

Alfergon
  • 5,463
  • 6
  • 36
  • 56
Todd W Crone
  • 1,185
  • 8
  • 23
4

I find that simply setting <Class>.metaClass = null works for me.

Spock Example:

def "mockStatic Test"(){
  given:
  RestClient.metaClass.static.postAsXml = {
    String uriPath, String xml ->
    return 65536
  }

  when:
  //some call that depends on RestClient.postAsXml

  then:
  //Expected outcomes

  cleanup:
  //reset metaclass
  RestClient.metaClass = null
}
dafunker
  • 519
  • 3
  • 9
  • Thanks - I'm using Spock too and setting Class.metaClass.'static'.method to null didn't work but setting Class.metaClass to null did. – tschumann Apr 06 '20 at 04:47
1

schmolly159's hint above led me to the following solution:

    def originalPostAsXml = RestClient.metaClass.getMetaMethod('postAsXml', [String, String] as Class[])

then to reset the method:

    RestClient.metaClass.'static'.postAsXml = { String uriPath, String xml ->
        originalPostAsXml.invoke(delegate, uriPath, xml)
    }
Noel Yap
  • 18,822
  • 21
  • 92
  • 144