Mocking collaborating Abstract class using EasyMock partial mocking


There are times when we need to unit-test methods of a concrete subclass, which colloborate with methods of the abstract superclass. The key requirement is that we want to unit-test the subclass methods in isolation; without bothering about the collaborating methods in the abstract superclass.

Lets take a look at a simple example : Dog extends Animal.

// Code for Animal

package com.xebia.example;

public abstract class Animal {

    public void sit() {}
    public void stand() {}
    public abstract void expressAnger();

    public void speak(String message, Integer volumeLevel) {
        // Here goes the code for the method to to speak-out the message with the given volume.
    }
}

// Code for Dog

package com.xebia.example;

public class Dog extends Animal {

    public static final String ANGRY_MESSAGE = "BARK-BARK";

    public void expressAnger() {
        speak(ANGRY_MESSAGE, 10);
    }
}

Test cases for Animal class have already been written. Now comes the turn of Dog class – how do we test expressAnger method of Dog class in isolation. Well, that's not hard with EasyMock class extension 2.2 partial mocking feature, which allows (as the name suggests) only selected methods of a class to be mocked.

If we take a look at the expressAnger method of Dog class, the only thing that we want to test is that speak method of Animal class is called with the right parameter values. So, we need to mock the colloborating method speak in order to unit-test expressAnger method.

Here is the EasyMock test-case which shows how this can be done.

// Code for DogTest

package com.xebia.example;

import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;

import java.lang.reflect.Method;

import junit.framework.TestCase;


public class DogTest extends TestCase {

    // Class under test. Also the colloborating class.
    Dog dog;

    protected void setUp() throws Exception {
        super.setUp();
        // Create a partially-mocked instance of Dog class. The "methods to be mocked" are passed as an array.
        dog = createMock(Dog.class, new Method[] {Dog.class.getMethod("speak", String.class, Integer.class)});
    }

    public void testExpressAnger() {
        // Set expectations.
        dog.speak(Dog.ANGRY_MESSAGE, new Integer(10));
        // Change mode.
        replay(dog);
        // Call the method under test.
        dog.expressAnger();
        // Verify expectations.
        verify(dog);
    }
}

The test-case is quite simple – instead of mocking out the dependency/collaborating class (the usual case), we have mocked-out the collaborating method.

Of-course, the same trick applies when you want to test one method of a class, which calls other methods of the same class. So, you want to keep normal behaviour of the methodUnderTest & mock other collaboratingMethods of the same class.

Conclusion

EasyMock partial mocking is quite handy for testing in circumstances where a method of a class calls method of the same or superclass; and you need to test methods of the class in isolation of other methods.

Comments (9)

  1. Peter Veentjer - Reply

    June 18, 2007 at 10:34 am

    Thanks Deepak for the useful information.

    I normally extract the logic so I can test it, but this result in a lot of clutter (although with closures the clutter can be reduced because you need less interfaces).

    But good to know how it works.. It will prevent long test methods (where all methods are tested and not just one in isolation).

  2. Lars Vonk - Reply

    June 18, 2007 at 1:52 pm

    Hi Deepak,

    Interesting technology but I think you should be cautious in using this technique for the following reasons:

    - Refactoring support - Lets say you want to rename the speak method. Because the test refers to the speak method with the method name as String if does not get refactored (not in Eclipse that is). The same goes for adding parameters.

    - Using this technique you wind up in focusing on what the method does instead of what it should do. I think (unit) tests should focus on what code should do.

    I would prefer to exhaustively test the superclass speak() method separately and briefly test the expressAnger() method (which will test the speak() method again) in the concrete subclass. This might cause some duplication, but I rather have that then all these mocks.

  3. Deepak Mittal - Reply

    June 21, 2007 at 5:22 am

    Hi Lars,

    Very valid & useful comments. Didn't have these potential issues in mind, when I wrote this blog. I agree that it might be better to have some duplicate tests than to mock the methods in this manner.

    But I assume there would be scenarios when we would be extending an abstract class in a third-party library in our application, though I could not recall an example quickly. In such cases, "partial mocking" seems to be a good option.

  4. Xebia Blog - Reply

    June 25, 2007 at 8:36 pm

    [...] If you're not overriding any methods, the EasyMock partial mocking feature, which was presented in this blog entry, works as a charm. However if we're extending a component, we probably want to override some of its methods and have it present a somewhat different behaviour. Let's take a look at a small sample: public class CustomTree extends org.apache.myfaces.custom.tree2.HtmlTree {   private String nodeId; [...]

  5. Nattu - Reply

    July 31, 2008 at 12:33 pm

    i am not able to run this testExpressAnger successfully. It gives me
    java.lang.NoClassDefFoundError: net/sf/cglib/proxy/Enhancer

    I used EasyMock2.3 and EasyMockClassExtension2.2.2

  6. Naveen Kumar - Reply

    August 18, 2008 at 9:01 am

    download
    cglib-nodep-2.1_3.jar from
    http://www.java2s.com/Code/Jar/Spring-Related/Downloadcglibnodep213jar.htm
    add into classpath, it will work.
    Hope this helps.

  7. Parag Lohiya - Reply

    March 22, 2013 at 5:19 pm

    I tried the above but with version 3.1 they have deprecated the method

  8. Parag Lohiya - Reply

    March 22, 2013 at 5:20 pm

    could you please guide me how to achieve it in 3.1

Add a Comment