Mocking static methods and the Gateway pattern

From AWiki

Jump to: navigation, search


A year ago I started to use mocking libraries (e.g., Mockito, EasyMock, ...), both for learning something new and for testing purpose in hopeless cases.

Briefly: such a library makes it possible to dynamically redefine the behaviour (return value, thrown exceptions) of the methods of the class under test, in order to run tests in a controlled environment. It makes it possible even to check behavioural expectations for mock objects, in order to test the under test class' interactions with its collaborators.

A few weeks ago a colleague asked me: "[How] can I mock a static method, eventually using a mock library?".

In detail, he was looking for a way to test a class whose code was using a static CustomerLoginFacade.login(String username, String password) method provided by an external API (an authentication custom API by a customer enterprise).

His code appeared as follows:

public class ClassUnderTest {
 	...
	public void methodUnderTest(...) {
		...
	 	// check authentication
 		if(CustomerLoginFacade.login(...)) {
	  	 	...
		} else {
	 		...
	 	}
	}
}

but customer's authentication provider was not accessible from test environment: so the need to mock the static login method.

A quick search in the known mocking libraries revealed that:

  • EasyMock supports static methods mocking using extensions (e.g, Class Extension, PowerMock)
  • JMock doesn't support static method mocking
  • Mockito (my preferred [Java] mocking library) doesn't support static method mocking, because Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change (see official FAQ). The same position appears even in a JMock-related discussion.

So, thanks to my colleague, I will analize the more general question "Ho can I handle external / legacy API (e.g., static methods acting as service facade) for testing purpose?". I can identify three different approaches:

  • mocking by library: we can use a mocking library supporting external / legacy API mocking (e.g, class' mocking, static methods' mocking), as discussed earlier
  • mocking by language: we can refer to the features of a dynamically typed programming language to dynamically change external / legacy API implementation / behaviour. E.g., the login problem discussed earlier can be solved in Groovy style, using the features of a language fully integrated with the Java runtime:
CustomerLoginFacade.metaClass.'static'.login = {
              return true;
};
  • Architectural approach: mocking by design. This approach refers to a general principle: hide every external (concrete) API behind an interface (i.e.: coding on interfaces, not on concrete implementation). This principle will be more widely discussed in a dedicated article.

So, we can solve my colleague's problem this way: first, we define a login interface:

public interface MyLoginService {
	public abstract boolean login(final String username, final String password);
}

Then, we refactor the original methodUnderTest code to use the interface:

public class ClassUnderTest {
 	private MyLoginService loginService;
	// Collaborator provided by Constructor injection (see here for a discussion about injection styles)
	public ClassUnderTest(final LoginService loginService) {
		this.loginService = loginService;
	}  	
	...
	public void methodUnderTest(...) {
		...
	 	// check authentication
 		if(loginService.login(...)) {
	  	 	...
		} else {
	 		...
	 	}
	}
}

So, for testing pourposes, we can simply inject a fake implementation of the MyLoginService interface:

public void myTest() {
	final ClassUnderTest cut = new ClassUnderTest(new FakeLoginService());
	cut.methodUnderTest(..., ...);
	...
}

where FakeLoginService is simply

public class FakeLoginService implements MyLoginService {
	public boolean login(final String username, final String password) {
		return true;
	}
}

and the real, pruduction implementation of the interface looks simply like this:

public class RealLoginService implements MyLoginService {
	public boolean login(final String username, final String password) {
		return CustomerLoginFacade.login(username, password);
	}
}

In practice, the interface defines an abstract gateway to the external authentication API: changing the gateway implementation, we obtain testing independence from the customer's authentication provider.

IMHO, i prefer the last approach: it's more object oriented, and after all... my colleague called me once the more OO person I know :-). I find this approach more clean and elegant: it's built only upon features of the programming language and doesn't refer to external libraries nor testing-support languages. In terms of design, too, I think it's a more readable and more reusable solution to the problem, which allows a clearer identification of responsibilities of the various pieces of code: MyLoginService defines an interface, and every implementation represents a way to implement it (a real-life (i.e.: production) implementation versus the fake one). Generally, I prefer strongly typed solutions, which allow a more complete compilation-time control over relationships between modules. Mocking solutions, both my library and by language, represent a way to break strong typing, which may lead to out of control situations like so-called [Spaghetti code].

Method mocking (by library or by language, doesn't matter) is in certain, specific situations a very useful technique, too: I'll discuss its application scenario in a later article.

[Incidentally: the solution adopted by my colleague was just that I have proposed (i.e., mocking by design)]

Credits: thanks to Samuele for giving me cause to analyze such a problem (and for our frequent and ever interesting design-related discussion). Thanks to my wife for hers valuable support in writing in English.

Personal tools