Groovy MetaClass Magic in Unit Tests

As you may or may not know, Groovy has the concept of MetaClasses. MetaClasses enable you to add or change methods at runtime. This is especially handy when doing some mocking during a unit test (or, in our case, GrailsUnitTestCases). By mocking methods, you can mock out certain dependencies or behavior which you might not want during a unit test. An example:

In this case, if you want to test the logic of the PersonAdapter without using the complex operations in the service, on way to test this by using some MetaClass magic, as can be seen below:

While this might be not be the optimal example or test (you might consider using Mocks instead), there’s a flaw in this test. Since the complexOperation is now metaClassed on the class level, instead of on the object, it means that future tests will also be affected, including Integration tests. This might lead to some very unexpected behaviors, which are sometimes hard to track down.

There are two ways to ‘fix’ this:

  1. Always call the super.tearDown() in tests. This will reset the Groovy meta registry to it’s normal state. However, you need to explicitly call ‘registerMetaClass’ in tests to allow Grails to clean this up. If you forget this, you’re metaClass change can affect other tests too. To prevent that, we have option 2.
  2. Use the MetaClassMixin.

The MetaClassMixin is a simple class we use in our project to automatically register metaClasses in the GroovySystem’s MetaClassRegistery. Since we are using only the MetaClassMixin to do metaclass magic instead of doing it directly on classes, we can never forget to register the class anymore. This has saved us in many cases. An example usage can be seen below:

Using the MetaClassMixin, you no long have to worry about registering your MetaClassed classes, or, more importantly forgetting to register them! Below, you can find the source of the MetaClassMixin (this mixin has been written by Levi Hoogenberg, so credits for that!). Feedback on this welcome of course!

Happy metaClassing!

8 replies
  1. Brian Doyle
    Brian Doyle says:

    Is MetaClassMixin something part of Groovy or Grails or do I need to add that into my codebase? Thanks and really good stuff!

    • Erik Pragt
      Erik Pragt says:

      Hi Brian, thanks for the reply. MetaClassMixin is something not included in Groovy or Grails, but you can just copy the above class into your project to be able to use it. If you run into problems, just let me know.

  2. Fanguang Meng
    Fanguang Meng says:

    Hi, Erik:

    Thanks very much for posting the website.

    1. I created MetaClassMixin class and copied and paste the code
    2. Then in my integration test, I tried to use metaClassFor, I added @Mixin(MetaClassMixin) before class.

    metaClassFor(MockReportCompleteDataStore).getSurveys = {appID, filter, maxResults, start->
    return getSurveys(appID, filter, maxResults, start)
    }

    Do you have any idea?

    Thanks very much.

    Fanguang
    I got the following error:
    groovy.lang.MissingMethodException: No signature of method: static idea.rest.server.tests.SurveyControllerIntegrationTest.metaClassFor() is applicable for argument types: (java.lang.Class) values: [class idea.store.MockReportCompleteDataStore]
    Possible solutions: metaClassFor(java.lang.Class), metaClass(groovy.lang.Closure)
    at idea.rest.server.tests.SurveyControllerIntegrationTest.setUpClass(SurveyControllerIntegrationTest.groovy:40)

    • Fanguang Meng
      Fanguang Meng says:

      I think the problem was I put the code in @BeforeClass setUpClass() method, after I put into @Before setup() method, the error message was gone.

      I tried put static before definition of metaClassFor() in MetaClassMixin, it didn’t work either.

Trackbacks & Pingbacks

Comments are closed.