Software Development Magazine - Project Management, Programming, Software Testing |
Scrum Expert - Articles, tools, videos, news and other resources on Agile, Scrum and Kanban |
Mockito - Open Source Java Mocking Framework
Tomek Kaczanowski, http://practicalunittesting.com
Mockito is a popular open source Java mocking framework. It is very powerful and easy to use. It gives a lot of power into the hands of developers, but at the same time does not corrupt their hearts! Instead it urges them to write clean, well-designed and loosely-coupled code.
Web Site: https://github.com/mockito/mockito Version Tested: Mockito 1.9.0 on Linux with Java 1.6.0_26 License & Pricing: Open Source (MIT License)
Using Mockito
Mockito is distributed as a single JAR file, which must be available in the classpath in order to execute tests. Usually this is provided by a properly configured build tool like Ant or Maven.
Why Test Doubles?
When writing tests it is often required to control the environment in which tested object (a so-called system under test (SUT), which can be a single class, a module or an application) is running. It is especially important to isolate the tested class in unit tests to make sure that it works as expected in every circumstances. It is often also required to verify if the tested object interacts with its collaborators in a particular way. Mock objects (or to be more precise - test doubles) allow to achieve both goals.
Consider a simple class - named WeatherForecast - which fetches weather forecasts from some external sources (e.g. web services). Now imagine writing tests for this class. It would be quite hard to test all its logic if we had to use the real collaborators (real web services). How can we make a real web service that will provide all the needed answers to test all possible paths of execution and thus test every bit of the WeatherForecast implementation? How can we verify how WeatherForecast class behaves when its collaborators behave unexpectedly - e.g. by breaking connections, responding with nonsense data or throwing exceptions? Making real web services behave in such awkward way might be really hard, if it is possible at all! Mock objects (or to be more precise stubs) allow us to test such classes, which rely on "external" collaborators.
The usefulness of mock objects is not limited to handling of such "external" collaborators. Following the Test-Driven Development (TDD) approach, we often write classes which use collaborators (in form of interfaces) that are not yet implemented. This is also a perfect place for using test doubles (mocks and/or stubs) to test this emerging design.
NOTE: Please note, that even testing with test doubles is usually not enough to prove your application is working correctly! Make sure you have also some end-to-end tests which test "the real thing"!
Mockito's Features
This short introductory article will describe what values Mockito can bring to your testing activities. I will concern less with the syntax which is described in details on Mockito's homepage and numerous websites.
Apart from "typical" tasks of a mocking framework (i.e. creation of test doubles, stubbing, setting expectations and verification of behavior), Mockito has some features which distinguish it from other mocking frameworks:
- Mockito allows to write test methods compatible with "arrange/act/assert" approach. This is different from what other mocking frameworks require, and feels much more "natural".
- Mockito can be used to write Behavior Driven Development (BDD)-style tests with some syntactic sugar that facilitates it.
- Mockito provides a nice, easily readable syntax. It also provides some annotations useful for reducing boilerplate code.
- It is easy to read Mockito's error messages. They also point out to possible mistakes a developer can make, which is very handy for people who begin to work with Mockito.
A lot of attention is given to the maintainability of tests. This is easier to achieve with Mockito than with other frameworks, by its following properties:
- Mockito is "forgiving" by defaults as it verifies only the interactions that it was asked to verify. This allows to write very focused tests that are not fragile. Mockito also makes stubs return some canned values by default like. zeros, empty strings and falseys.
- Mockito allows writing of relaxed tests. Developers can specify what is really important for the given test scenario, and abstract over the unimportant details, using constructs like anyString() instead of specific values for instance.
It is worth mentioning that Mockito allows also for verification of quite sophisticated scenarios. For example, it can be used to:
- verify the number of method calls (using its times() method),
- verify the order of calls to particular collaborators (e.g. first, method a() was called on collaborator A, then method b() was called on collaborator B),
- inspect the arguments of methods created within the tested methods (using ArgumentCaptor class),
- apply "partial mocking" technique.
Some of the above should be use with extreme caution as they can hurt tests readability and/or maintainability! Mockito's documentation explains this in detail.
An Example
As an example of Mockito let us consider the following, simple class:
public class WeatherForecast { private WeatherService globalWeather; [1] private WeatherService localService; [1] public WeatherForecast(WeatherService globalWeather, WeatherService localService) { this.localService = localService; this.globalWeather = globalWeather; } public Weather getForecast(String city) { [2] if (localService.hasForecastFor(city)) { return localService.getWeather(city); } return globalWeather.getWeather(city); } }
This simple class has two collaborators that are both of the WeatherService type [1]. When asked for a weather forecast [2], it first queries the local forecast service, and only if it can not provide a forecast, refers to the global weather service. This logic is quite typical for the citizens of object-oriented systems, in which each class has a very limited responsibility and often relies on others to perform its duties.
Now let us see a test code, which verifies one of the possible execution paths of the WeatherForecast class.
@Test public void shouldFetchWeatherForecastFromGlobalServiceIfNotAvailableLocally() { // creation of collaborators [1] WeatherService localWeatherService = Mockito.mock(WeatherService.class); WeatherService globalWeatherService = Mockito.mock(WeatherService.class); // creation of SUT [2] // and injection of collaborators WeatherForecast forecast = new WeatherForecast(globalWeatherService, localWeatherService); // stubbing of collaborators // telling them what should they do when asked [3] Mockito.when(localWeatherService.hasForecastFor(anyString())) .thenReturn(false); Weather forecastedWeather = new Weather(); Mockito.when(globalWeatherService.getWeather(anyString())) .thenReturn(forecastedWeather); // invocation of the SUT method [4] Weather weather = forecast.getForecast("myCity"); assertThat(weather).isNotNull(); [5] assertThat(weather).isSameAs(forecastedWeather); }
This test presents all the typical phases of Mockito test. First the collaborators are created [1]. Mockito's mock() method is used (in reality I would rather import it statically so the Mockito. part could be omitted). It results with the creation of an imposter who looks like a real object of the WeatherService type, but can be strictly controlled. This is exactly why we use Mockito to create it.
[2] Then the object we attempt to test - of the WeatherForecast type - is created. Its collaborators are injected via its constructor.
Now, to test a specific scenario we need to instruct the collaborators on what they should do. Let us test that if the local weather service is unable to provide a forecast, then a global service is used. In order to do this, we need to inform both weather services about their expected behavior [3].
After every object knows its role, we can call the actual method of the SUT [4] and verify that it behaves properly [5].
Now comes the verification part. In this case, it is enough to verify that the returned Weather object is the same as the one which the global service should provide.
As we can see the expectations part [3] are really readable. For example one can read the following line:
Mockito.when(localWeatherService.hasForecastFor(anyString())).thenReturn(false);
like this:
"When someone asks you, localWeatherService if you can provide weather forecast for any city, you will answer with no."
Please note that this test is very careful not to verify too much. The main reason for this is not to bind the test too strongly to the current implementation of the WeatherForecast class. If required, we could use one of the methods provided by Mockito to strictly control what interactions happened during the execution of SUT methods. We could for example write the following additional assertions:
- Mockito.verify(globalWeatherService).getWeather("myCity"); - to verify that the getWeather() method was actually called on globalWeatherService collaborator.
- Mockito.verifyNoMoreInteractions(localWeatherService) - to make sure that apart from calling the hasForecastFor() of the localWeatherService collaborator, its no other methods were called.
Such verifications are often required but should be used with caution. As a rule of thumb, whenever possible we should use state testing and not interactions testing that verifies the methods called on collaborators. This means that we should assert on the returned values or changed states of objects.
Limitations
Even if Mockito is very powerful, useful and easy-to-use, it still has some limitations. They result from the conscious decision of its designers who created a tool aimed at working with well-designed code. Mockito shines when used against well encapsulated, loosely-coupled code. However, if you work with a dreadful spaghetti code filled with tight-coupled classes, Mockito will not provide means sufficient to deal with it. There is no support for mocking of static or final classes. Some will say this is a weakness of Mockito, while others will claim that this is an important feature of this tool, which makes developer write better code or redesign/refactor existing one.
Conclusion
If you look at the frameworks' popularity, you will notice that Mockito is very popular in Java world (if not a number one!), and still gaining popularity. It is has already proved its usefulness and robustness in thousands of open-source and commercial projects. It is frequently released, and is still being improved. It can be used with other popular tools like JUnit, TestNG, Cobertura, Hamcrest or FEST Fluent Assertions. Last, but not least, it has a very friendly community with an active mailing list, and impressive documentation.
I would like to end this article with a quote from Mockito's website, which very well describes this superb tool: "Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with clean & simple API. Mockito doesn't give you hangover because the tests are very readable and they produce clean verification errors."
Further Readings
Mockito documentation https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
Practical Unit Testing with TestNG and Mockito, book by Tomek Kaczanowski, http://practicalunittesting.com
Szczepan Faber's blog http://blog.mockito.org/
More Software Testing and Java Resources
Click here to view the complete list of tools reviews
This article was originally published in the Summer 2012 issue of Methods & Tools
Methods & Tools Testmatick.com Software Testing Magazine The Scrum Expert |