Overriding Java methods in Groovy for unit testing

Lately, I’ve been experimenting with writing unit tests in Groovy to test our Java code at Orbitz. Groovy provides many nice language features that have the potential to dramatically reduce the amount of test code that you have to write, and to decrease the maintenance burden of that test code. Since it makes tests easier to write and maintain, hopefully more people will do it! :)

One of the features of Groovy that immediately caught my attention was the ability to specify a new implementation for a method of a given class at runtime. I was first introduced to such power by Ruby, and have been wanting it elsewhere ever since. I immediately thought that this would be great for testing our legacy code, where stubbing methods is difficult, yet often necessary to avoid major refactoring work. Changes involving major refactoring take much more time and increase the possibility of introducing more bugs. This codebase is in the middle of being swapped out with a new one, so a major refactoring isn’t really worth the effort (it would however be worth the effort in the new codebase). Currently, to stub out private methods buried in our codebase, we’ve been widening the accessibility of the method to protected, and subclassing the class to override the functionality. I’m sure this seems hacky, but it’s really less risky/evil then some of the alternatives. The code under test in this case is back end code that is not available outside of our team. Obviously, widening the scope on a method of a released API or library would be a different scenario entirely. So, I was really excited to think that Groovy could possibly help us with this nastiness.

Sound too good to be true? Well, it is.

This great feature is only available for Groovy objects! Java objects need not apply. You can add methods to Java classes, but you can’t change them. Bummer.

While scouring the web to see if there was any other way to do this, I stumbled upon JMockit, a library that helps with unit testing in Java. JMockit lets you do exactly what I want to do; provide a new implementation for any method on a class! Private methods, static methods…no sweat. JMockit to the rescue!

Right? Wrong.

Exception in thread "main" mockit.RealMethodNotFoundForMockException:
Corresponding real methods not found for the following mocks:
groovy.lang.MetaClass getMetaClass(),
Object invokeMethod(String, Object),
Object getProperty(String),
void setProperty(String, Object),
void setMetaClass(groovy.lang.MetaClass)

With JMockit, the mock object containing the new implementation must not include any new methods that are not on the original class. The problem here is that all objects in Groovy contain several methods that are not on java.lang.Object. So, even though you are not programmatically adding methods to the mock that aren’t on the original, since your object is automatically a GroovyObject, it has these new Groovy methods, whether you want them or not. And, specifically extending java.lang.Object doesn’t help either. So, back to square one.

But, all hope is not lost. Sure, it sucks that we’re reduced back to widening the accessibility of a method so it can be stubbed out in a subclass, but at least doing this is much easier in Groovy. Take the following Java class for example:

public class Book { 
    public String read() { 
        return getLine(); 
    }   

    private String getLine() { 
        return "Damn this is a long book!"; 
    }   
} 

What if we want to have getLine return something different? Well, first we have to make it protected:

    protected String getLine() { 
        return "Damn this is a long book!"; 
    }   

Then, we have to subclass Book and provide the new implementation. The easiest way to do this in Java would be with an anonymous inner class:

Book b = new Book() { 
    protected String getLine() { 
        return "Short book"; 
    } 
}; 
assertEquals("Short book", b.read()); 

This is much more concise in Groovy, thanks to closures, map coercion, and the fabulous “as” keyword:

def b = [ getLine: { "Short book" } ] as Book 
assert "Short book" == b.read() 

Not only is it less code, but it also protects us from interface changes to getLine! getLine can be changed to take a parameter or throw a new checked exception, and our test would remain unchanged, lessening the maintenance burden of our test case. Very nice.

So, although this is not ideal, it still has benefits over its Java counterpart.

Be Sociable, Share!

    13 thoughts on “Overriding Java methods in Groovy for unit testing

    1. Nice post John, but what if getLine() takes an argument, says getLine(int), then what would be the right Groovy syntax to override it ?

      This is the 3rd times I tried to post a comment here, don’t know why I cannot see my comment anywhere …

      Thanks,
      David

    2. Hey David, thanks for the comment. Sorry about any trouble you had posting. This was the first time WordPress notified me of a pending comment.

      I changed the Book class to look like this:

      public class Book {
          public String read() {
              return getLine(1000);
          }
      
          protected String getLine(int lines) {
              return "Damn this is a long book!.  It has " + lines + " lines";
          }
      }
      

      And the test still worked as is. Groovy was able to figure it out. However, I then changed the Book class to look like this:

      public class Book {
          public String read() {
              return getLine(1000, 10000);
          }
      
          protected String getLine(int lines) {
              return "Damn this is a long book!.  It has " + lines + " lines";
          }
      
          protected String getLine(int lines, int words) {
              return "Damn this is a long book!.  It has " + lines + " lines and " + words + " words";
          }
      }
      

      And, I got the following error while running the test:

      Caught: groovy.lang.MissingMethodException: No signature of method: TestBook$_run_closure1.call() is applicable for argument types: (java.lang.Integer, java.lang.Integer) values: {1000, 10000}
      at Book_groovyProxy.getLine(Script1.groovy:8)
      at TestBook.run(TestBook.groovy:2)
      at TestBook.main(TestBook.groovy)

      To fix it, I modified the test to specify the parameters of the method it was overriding, like so:

      def b = [ getLine: { lines, words -> "Short book" } ] as Book
      assert "Short book" == b.read()
      

      That did the trick.

    3. Anytime David. I’m not doing full time Java development at the moment, but if I were, you can bet I’d be mixing it up with some Groovy. If you’re serious about learning Groovy (and I think that any Java developer should be), then I’d highly recommend Manning’s Groovy In Action.

    4. Hi John,
      I tried to do the same override to a Java Generics class like below to override isMatching(JButton button), the attached Java and Groovy code snippets show what I did but the Groovy code spit error because it could not find which method to invoke. Does Groovy handles method override in a Generics class ?

      Thanks,
      David

      // Java
      public abstract class GenericTypeMatcher extends AbstractComponentMatcher {
      private final Class supportedType;

      public GenericTypeMatcher(Class supportedType) {

      }

      public GenericTypeMatcher(Class supportedType, boolean requireShowing) {

      }

      protected abstract boolean isMatching(T component);
      }

      GenericTypeMatcher textMatcher = new GenericTypeMatcher() {
      @Override protected boolean isMatching(JButton button) {
      return “OK”.equals(button.getText());
      }
      };

      // Groovy
      def b = [ isMatching: {button -> "OK".equals(button.getText())} ] as GenericTypeMatcher
      b.click()

      # will generate error below:
      Error casting map to org.fest.swing.core.GenericTypeMatcher, Reason: Could not find which method () to invoke from this list:
      public org.fest.swing.core.GenericTypeMatcher#(java.lang.Class)
      public org.fest.swing.core.GenericTypeMatcher#(java.lang.Class, boolean)

    5. Looks like the problem here is that GenericTypeMatcher does not have a no-arg constructor. I do not believe the as keyword can be used to coerce a closure into a class if that class does not have a no-arg constructor. So, looks like this technique is limited to overriding methods in interfaces, and classes with no-arg constructors.

    6. @John Wood

      Hi John,

      When you added a parameter to getLine(int lines), you say the test worked. Does Groovy runtime automatically invoke that method with a null in that case?

      Also when you added another method with 2 parameters, the test dod not work. Why did this happen? Was it because the Groovy runtime could not figure out which of the methods to call?

      I tried a little experiment myself. Like you I added a parameter to a method which did not take any parameters earlier. The client code continued to work with the method getting a null for the parameter. However, when I added one more parameter to the SAME method, the client code started failing. So, looks like Groovy’s runtime will add one null to the method invocation, but not two.

      I wonder why?

    7. Hi Parag,

      The Groovy runtime didn’t invoke getLine, Book’s read method did. So, it passed 1000 as the parameter to my overridden getLine method. I modified the code above and tested it out to confirm. Keep in mind that you are not invoking getLine on the first line of the Groovy script, you are overriding it.

      I’m not sure why you were getting null passed into your method. Feel free to post the code here, and I’d be glad to take a peek.

      And, I believe your second point is correct. It looks like if the method you are overriding only takes one parameter, you do not need to specify that parameter when overriding the method in a closure. However, if your method takes more than one parameter, then you need to. This appears to be the reason why we both got errors when trying to override a method with more than one parameter, with explicitly stating those parameters in the closure.

      I should state that I’m not a Groovy expert by any means :)

    8. @John Wood
      Hi John,

      Thanks for your reply. I guess the code is really small and simple, so I hope I have not missed something.

      someMethod()

      def someMethod(i) {
      println(“””${i}”””)
      }

      This outputs null


      Thanks
      Parag

    9. Oh, I see. Your case was a bit different from mine. Yeah, it does appear that Groovy does a little magic to automatically pass in null to a one argument method if no parameter is specified. Interesting.

    10. Hi,

      I’m new to groovy and I’m trying to do something close to your example, but I’m getting the following exception

      java.lang.Exception: Unexpected exception, expected but was
      Caused by: java.lang.IllegalAccessError: tried to access method com.mypackage.IndexManager.()V from class IndexManager_groovyProxy
      at IndexManager_groovyProxy.(Script1.groovy:4)
      at java.lang.reflect.Constructor.newInstance(Constructor.java:501)
      at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
      at

      My java class is as follows…

      public class IndexManager
      {

      private IndexManager() throws WplexInfoError
      {
      loadSpellCheckDictionary();
      }

      public static IndexManager getInstance() throws Exception
      {
      if (instance == null)
      {
      instance = new IndexManager();
      }

      return instance;
      }

      private void loadSpellCheckDictionary() throws Exception
      {
      try
      {
      spellDirectory = getSpellFSDirectory();
      }
      catch (IOException e)
      {
      throw new MyException();
      }
      }

      private FSDirectory getSpellFSDirectory() throws IOException
      {
      return FSDirectory.open(new File(“myfile.txt”));
      }
      }

      and the groovy test case is this

      class IndexManagerGroovyTest
      {
      @Test(expected=IOException)
      void loadSpellCheckDictionaryException()
      {
      def manager = [ getSpellFSDirectory: { throw new IOException() } ] as IndexManager
      manager.loadSpellCheckDictionary()
      }

      }

      Can’t I override the private method? Does it only work for interfaces? Or I did something else wrong?

      Thanks

      Kelly

    11. Hi Kelly. Sorry, but I haven’t done a whole lot of Groovy lately, so I’m afraid I can’t answer your question off the top of my head. Stack Overflow is a great place to ask questions like this.

    12. Pingback: Proxy A Groovy Ant BuildListener? | T. C. Mits

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>