The following tutorial should give an overview about the functionality of mocking frameworks and why you should use them to write better tests. The popular mocking framework for Java, Mockito, will be used as a practical example.
What is Mocking?
Mocking in general describes the copying of something else behaviour. In the context of object-oriented programming languages do you want to fake the behaviour of an object without having to create a real instance of the object. But why should you want to do something like this?
Good code should also be well tested. Many of those tests will be unit tests, which should verify the behaviour of a single component, without relying on dependencies to other objects. The following picture illustrates this well.
By mocking the needed objects are you able to isolate the component that you want to test. Now it is easier to locate a possible error, because only the test of the broken component will fail if something goes wrong.
There are two different methods of mocking, proxy-based mocking and class loader remapping. Mockito uses proxy-based mocking.
I chose to work with Mockito for this tutorial because it is the most used mocking framework for Java and one of the most used Java libraries in general. It has many advanced features, but it is also easy to write good tests with just the basic tools. When you encounter a problem, then the possibility that someone already solved it or something similar, is extremely high. Also, the general concept of working with proxy-based mocking should be transferable to any other mocking framework of your choice.
How to use Mockito
To use Mockito do you have to include the Mockito library and a testing framework into your project. I will use Junit 5 as my testing framework. For an easy setup did I use Maven. You can find the necessary Maven packages with the following addresses:
Then all you need is to make the following imports at the beginning of your test class, and you are ready to write unit tests with mocks.
In the following part I will explain some of the basic functionalities of Mockito. For better visualization did I write a little test project where I showcase the explained functionalities. You can find the source code in the following git repository:
It contains a simple library and book class. The different tests should cover the functions of the library class and I will always mock the book class to give an example how you can work with Mockito. When writing about a certain feature, I will be referring to the test class where it is implemented.
Create a mock an set its behaviour
Let us begin with the most important part. How do I create a mock of the needed class? This part is very simple. You just call the static method mock(MyClass.class) to create a new mock object. Now you must define the behaviour of your mock object when certain functions are called on the object.
This can be realized with when(mock.doSomething()).thenReturn(xyz) and is called stubbing. With this you can create the necessary environment that is needed for your test to work. You can find this in every test class, but in its most basic form can you see it in the class TestSumOfPages.java.
Now you are already able to get your test working without dependencies to other classes than the on that should be tested. But we are still not verifying if the behaviour of our class is as intended. First you can use the utilities of JUnit in form of the different Assert-functions to verify the output of the tested methods. But Mockito also provides you with some extra tools, so you can write even better tests.
Verification with Mockito
You can verify if the functions of a mock object have even been called within your test. This can be helpful to see if the code is executed as intended and does not only somehow gives the correct results back. For this you can use the verify() function. You can also specify how often a function should have been called, either with an exact number or a minimum or maximum. In the class TestAvgPerChapter.java can you find a simple example for the use of the verify() function.
In this class can you also find an example for the InOrder feature. You have to create a new InOrder() object with the mock objects that have to be verified as arguments to make use of this. Then you can call the verify() function on the InOrder() object to check if the functions have been called in the same order as you verify them. This can be helpful in multithreading scenarios when certain functions must be called before the rest. The given example is not very practical but shows how to use the functions.
Alter the behaviour of the mock while testing
Sometimes you also want to test your code under different scenarios. Then you must change the behaviour of your mock multiple times. In theory you can do this by stubbing the needed function again with a different return value after the first use. In big tests this can become very tedious and blows the code up. That’s why there are different options to control the behaviour of your mock for different calls.
The most simple variant is the reset() function. When you call this on a mock, all stubbing will be reset to the default value. You can find a simple example for this in the test class TestAvgChapterPerBook.java in the function testAvgChapterPerBook().
A little bit more advanced is the usage of consecutive stubbing. With this feature do you define the return value for every call at once. You can either do this by chaining doReturn() statements or by giving several arguments. They will then be executed in the defined order. An example can be found in the class TestListOfAuthors.java. Consecutive stubbing can be helpful when you want to test different scenarios in one test. You then have a clear separation of the test code and the definition of you mock object.
At the end I want to give a brief introduction into the topic of partial mocking of real objects. The basic idea is that you use a real instance of the needed object for testing and only stub selected functionalities, where necessary. In the past this was considered a bad practice and a code smell because the reason you are using mocks is just to avoid using the real object. But there are several use cases where this can be necessary, like when working with legacy code or third-party interfaces.
When you want to utilize this feature you can use the spy() method to create your mock object. You need a real instance of the object to mock as an argument. Then you can use your mock instance as usual. The difference is now that when you are not stubbing a function then the function of the real object will be called instead of a default null value. An example for this can be found in the class TestAvgChapterPerBook.java and the function testWithSpy().
Thank you for reading this little tutorial about Mockito. I hope it could help you to get a first impression on how to work with mocks. Every feedback is appreciated.