Mocking Static Calls

How can you test methods that contain static method calls? This is a question we're facing at the moment while working on an application of which parts have been written by another development team. In order to gain insight into functionality and quality of the codebase, we are writing JUnit tests. But a lot of the code is dependent on either the JSF FacesContext or the Spring ApplicationContext being available.

It is however impossible to mock static method calls like FacesContext.getCurrentInstance(). So how do we go around testing these calls out of container? We've thought up three different approaches, but each has its pros and cons. Let us first take a look at these three approaches...

1. Extract method
Introduce a method in each class that looks like the following:

protected FacesContext getFacesContext() {
	return FacesContext.getCurrentInstance();
}

This can be easily mocked out in your test by creating the class with an overridden method, as follows:

FacesContext mockContext = EasyMock.createMock(FacesContext.class);
new MyObjectUnderTest() {
	protected FacesContext getFacesContext() {
		return mockContext;
	}
}

Pros

  • Easy to refactor using extract method

Cons

  • Lots of code duplication

2. Static Helper
Our second option is to introduce a static helper class which has the following implementation:

public class FacesContextHelper {
	private static FacesContext context;

	public static FacesContext getCurrentFacesContext() {
		return context != null ? context : FacesContext.getCurrentInstance();
	}

	public static void setFacesContext(FacesContext facesContext) {
		context = facesContext;
	}

	public static void reset() {
		context = null;
	}
}

Now in a testclass we can just write down the following line to mock out the FacesContext:

FacesContextHelper.setFacesContext(EasyMock.createMock(FacesContext.class));

Pros

  • No real painful code-duplication
  • Easy to do search-replace to convert all calls to FacesContext.getCurrentInstance() to FacesContextHelper.getCurrentFacesContext().

Cons

  • Never forget to call reset() in the teardown of your test to prevent another test from picking up your mock.
  • Doesn't feel right to write static setter methods in your code just to enable testing it. Not an OO approach?

3. OO Helper class
Our third and last option is to introduce an interface and implementation of the FacesContextHelper:

public interface FacesContextHelper {
	FacesContext getCurrentFacesContext();
}

public class FacesContextHelperImpl implements FacesContextHelper {
	public FacesContext getCurrentFacesContext() {
		return FacesContext.getCurrentInstance();
	}
}

We now need to introduce an instance of the FacesContextHelperImpl to each class. For this example, we will use a package protected variable in each class. It is of course also possible to use a setter method. For our test cases we now need to introduce a new FacesContextHelper:

public class MockFacesContextHelper implements FacesContextHelper {
	private FacesContext mockContext;
	
	public MockFacesContextHelper(FacesContext mockContext) {
		this.mockContext = mockContext;
	}

	public FacesContext getCurrentFacesContext() {
		return this.mockContext;
	}
}

In our test cases we can now easily mock out the FacesContext again by setting the package protected field:

someClazz.facesContextHelper = new MockFacesContextHelper(EasyMock.createMock(FacesContext.class));

Pros

  • Feels more natural and OO than the static helper solution

Cons

  • Need to introduce a new field to each and every class which needs the FacesContext mocked out.

That's it. I myself am still doubting as to which method is the lesser evil, not one feels absolutely right. What do you think, which method do you consider better? Did I overlook another option perhaps?

Comments (16)

  1. Ricky Clarkson - Reply

    June 21, 2007 at 11:51 am

    Would there not be some bytecode manipulation that you could do, possibly via aspects, etc.?

    In Scheme, you can temporarily change what a function does. It's a shame Java is a step backwards (or a step towards early binding).

  2. Debasish Ghosh - Reply

    June 21, 2007 at 1:34 pm

    Just saw this the other day at Javalobby newsletter. Have a look at http://amock.blogspot.com/. I have not used it, but they do mock statics using aspects.

  3. Lars Vonk - Reply

    June 21, 2007 at 1:38 pm

    I prefer the OO Helper option in combination with AOP. Introducing a new field in every class is IMHO a crosscutting concern which is typically something you can solve with AOP.

  4. Deepak Mittal - Reply

    June 22, 2007 at 3:11 am

    I prefer the Static Helper class.

    It sounds OK to me to call the intercepting FacesContextHelper class, instead of calling the FacesContextHelper directly. However, we can use AOP to achieve the same thing.

    If the JSF API was designed keeping testability in mind, we might have received this mechanism out of the box.

  5. steve - Reply

    June 23, 2007 at 8:00 am

    I like that you are trying to do this but I thought I would mention that this highlights the fact that static methods like the ones you are dealing with are a bad idea specifically because they make code inflexible and hard to test. Ok, I'll get off my soap box now.

  6. Keith Sader - Reply

    June 23, 2007 at 3:22 pm

    I'll have to agree with Steve. This is a design wart of Faces which inflicts itself on every backing bean.

  7. Jeroen van Erp - Reply

    June 24, 2007 at 6:08 pm

    Steve & Keith,

    Thanks for the comments. I totally agree that static calls make code inflexible. However they are part of life when using frameworks such as JSF (FacesContext), or Spring (WebApplicationContextUtils), etc. I agree that we should try not write them, but not using them is sometimes not an option. Testing your own code (preferably) in isolation can thus be a requirement which is hard to meet.

  8. Paul Bakker - Reply

    July 10, 2007 at 2:06 pm

    There is a nice framework that allows you to replace code with mocks by doing bytecode modification. The framework is called JMockit, and can be found here: https://jmockit.dev.java.net/.

    It allows you to replace static, private/protected and final methods, and also non-default constructors. I showed some examples in a blogpost a few weeks ago: http://blogs.infosupport.com/blogs/paul_bakker/archive/2007/04/23/Mocking-unit-tests-with-JMockit.aspx

    The big pro of this way of working is that you do not need to modify your code in any way (introduce helpers etc.).

  9. Tomek Kaczanowski - Reply

    July 7, 2008 at 10:06 pm

    hi, this kind of code can be tested in many ways - some of which (e.g. jmockit, jeasytest) I described here : http://kaczanowscy.pl/tomek/?q=node/66

    cheers
    Tomek Kaczanowski

  10. Faces Context - Reply

    July 9, 2008 at 2:19 pm

    Why are you using FacesContext statically? You should be using dependency injection. This is why we're using JSF in the first place, right? In a managed bean add your set/getFacesContext, inject ${facesContext}. Happy mocking.

  11. Dag - Reply

    December 29, 2008 at 12:46 pm

    The line: FacesContext mockContext = EasyMock.createMock(FacesContext.class);

    Gives me an error because FacesContext is not an interface...(EasyMock 2.4)

  12. Jeroen van Erp - Reply

    January 7, 2009 at 8:47 am

    Dag: Consider using the easymockclassextension, that will allow you to mock out classes for which you do not have an interface.

  13. Swarm of XeBees » Mocking static methods - Reply

    June 30, 2010 at 10:08 am

    [...] blog at http://blog.xebia.com/2007/06/21/mocking-static-calls/ mentions numerous ways that enable a developer to mock static calls, however, with the advent of [...]

  14. Ajay - Reply

    November 16, 2012 at 7:57 am

    Hi ,
    I was able to successfully create a mock of FacesContext using EasyMock but the issue that while doing something like this :

    facesContext.getELContext().getELResolver().getValue(
    facesContext.getELContext(), null, beanName);

    i am getting null pointer exception while trying to retreive ELContext.

    Any ideas how i can approach such scenarios.

    • Jeroen van Erp - Reply

      November 16, 2012 at 11:00 am

      Hi Ajay,

      Once the FacesContext is a mock, you of course need to tell it what to return when the FacesContext.getELContext() is being called. If you don't the default of 'null' will be returned, and you'll get a NPE. So for instance:

      expect(mockFacesContext.getELContext()).andReturn(createMock(ElContext.class));
      // etc
      replay(mockFacesContext)
      // your test code.

      Please read up on the easymock site, or the mocking framework you're using for other options.

  15. Reinaldo - Reply

    January 23, 2014 at 12:13 pm

    Thanks for finally writing about >Mocking Static Calls <Liked it!

Add a Comment