Mocking Super Calls

Another installment in our mocking adventures. After I presented you last week with some possible solutions when you're mocking static method calls. Today I am facing a different problem.

JSF is a framework which is built around the idea of (reusable) components. If we want to introduce a new component, it is possible (and sensible) to extend one of the existing components, which takes away some of the hard work. However, we don't want to know too much the internals of the component we're extending, especially not if we're testing it. Actually we should be able to safely assume the base component has been thouroughly tested.

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;

  @Override
  public Object saveState(FacesContext context) {
    Object[] state = new Object[2];
    state[0] = super.saveState(context);
    state[1] = nodeId;
    return state;
  }

  public void setNodeId(String nodeId) {
    this.nodeId = nodeId;
  }

  // remaining implementation omitted.
}

How do we test the CustomTree.saveState method? We don't want to have to deal with what the superclass does to the (mocked) FacesContext in the test. Preferably we would like to write the following unit test:

public class CustomTreeTest extends TestCase {
  private CustomTree customTree;
  private FacesContext facesContext;

  public void setUp() {
    facesContext = EasyMock.createMock(FacesContext.class);
    // TODO: create new (partially mocked) instance of customTree.
  }

  public void testShouldSaveNodeIdInState() {
    customTree.setNodeId("someId");
    verify(facesContext);
    Object[] state = (Object[]) customTree.saveState(facesContext);
    replay(facesContext);
    assertEquals("someId", state[1]);
  }
}

Sounds easy enough, right? But notice the big TODO in there. How do we create an instance of CustomTree? Let's try the simplest thing first.

customTree = new CustomTree();

This however results in a stacktrace, which will more or less look like this:
java.lang.NullPointerException
at org.apache.myfaces.custom.tree2.UITreeData.saveState(UITreeData.java:91)
at org.apache.myfaces.custom.tree2.HtmlTree.saveState(HtmlTree.java:58)
at com.xebia.jsf.CustomTree.saveState(CustomTree.java:58)
at com.xebia.jsf.CustomTree.testShouldSaveNodeIdInState(CustomTreeTest.java:38)

The NullPointerException occurred because the UITreeData class wants to grab state from a DataModel. However we haven't set the DataModel onto our CustomTree as it isn't needed directly in the method we're testing. If we manage to fix this, surely we will encounter another "missing" dependency, which isn't of any use to the code we're testing. So we can conclude that this was not the preferred way of creating the object under test. But what can we look at then?

1. Indirection
If we reduce this problem to one which we already have a solution to, we're done. We can introduce an indirection, to wrap the super.saveState(FacesContext) call in. The resulting class could then look like this:

public class CustomTree extends org.apache.myfaces.custom.tree2.HtmlTree {
  private String nodeId;

  @Override
  public Object saveState(FacesContext context) {
    Object[] state = new Object[2];
    state[0] = saveSuperState(context);
    state[1] = nodeId;
    return state;
  }

  protected Object saveSuperState(FacesContext context) {
    return super.saveState(context);
  }

  public void setNodeId(String nodeId) {
    this.nodeId = nodeId;
  }

  // remaining implementation omitted.
}

With this indirection in place, it is suddenly easy to use either the EasyMock partial mocking feature, or create an instance for testing as follows:

customTree = new CustomTree() {
  @Override
  protected Object saveSuperState(FacesContext context) {
    return new Object();
  }
}

Pros

  • Easy to implement using a simple refactoring like "extract method" in Eclipse

Cons

  • The code under test needs to be adapted, and becomes more "aware" of testing issues

2. AspectJ / AOP
Using a cleverly written aspect, it might be possible to mock out the super.saveState(FacesContext) call. I haven't actually tried this, but am interested to see whether any of you can come up with one.

Pros

  • Code under test doesn't need to be adapted

Cons

  • If AspectJ weaving isn't enabled for your project, you need to integrate it into your build and IDE
  • Many projects don't want to use compile-time weaving, because weaving alters your written code, and thus unintended side-effects can be introduced if not used correctly

Can anyone come up with a pattern which I missed? Or a cleaner solution than either of the two presented here? Again not one of the two feels right. And at the moment, I haven't found a feature in EasyMock (or any other mocking framework) which deals with this problem nicely.

Comments (1)

  1. Steven Barendregt - Reply

    February 20, 2008 at 7:08 pm

    The problem remains that in the end you are still not sure if the CustomTree class calls the saveState method on the superclass.
    Some colleague programmer could modify the line 'return super.saveState(context);' in the CustomTree.saveSuperState method to 'return null;' and no one (or no unit test) would notice.

    You have only tested that the CustomTree.saveState method calls the new method 'saveSuperState' on it's own instance.

    I'm facing the same problem now, but I don't either have a solution at the moment.

Add a Comment