Mockito’s Solution to Avoid Mock Chained Statement Cascades

Francisco Maria
5 min readNov 2, 2022

--

Hi there, dear reader! Today I bring you a brief article presenting a useful Mockito feature that prevents an ever growing cascade of mock chained statements that may blow out of proportion and affect both the readability and maintainability of your test-specific code.

The idea of this article is to guide through a concrete example in order to better understand the issue with chained method calls and how they impact both the writing and execution of unit tests as well as how to improve the readability of such tests cases whenever such chained method calls can not be removed (e.g. legacy code).

Mocking: The Idea

Unit testing is critical to ensure that your individual units’ logic behaves as expected. Within the many existing software testing concepts, mocking stands-out as one of the most relevant and adopted strategies to guarantee the correctness of a unit’s logic with regards to its dependencies behavior.

The core of this concept relies on the isolation of the tested unit by mocking/replacing its dependencies with objects whose behaviour is explicitly manipulated, thus offering assertion support on the unit’s behaviour with regards to the (manipulated) behaviour of its dependencies.

The following code snippet exemplifies the usage of mocking for a simple garage service Java application. The code shall be self-explanatory but briefly, there is a central garage service which serves incoming repair cost requests by calling the dedicated sub-services (dependencies) regarding the type of repair requested.

Basic Mocking Example — Garage Service (repo link)

This simple example shows how powerful mocking can be by manipulating the unit’s (Garage) dependencies (TireService,EngineService) behaviour to test specific outcomes/return values of their interaction.

The testing frameworks used in the above example were both JUnit5 (testing execution framework) and Mockito (mocking-specific framework). The latter is a mature Java framework which offers solid mocking capabilities and that has proven to be a main actor when considering mocking features in Java applications (frequently coupled with JUnit).

Mock Chained Statements Cascades: An Ever-Growing Statement List

Whenever writing test code, one shall guarantee that it gets the same treatment and attention as the source code. In fact, many source code issues may be found (including logic bugs or architecture defects) when properly writing and executing unit tests.

One of such issues is mock chaining. In a nutshell, mock chaining occurs whenever there is a method call chain inside of a unit that needs to be tested. As previously stated, in order to guarantee isolation one can recur to mock the tested unit’s dependencies and hence manipulate their behaviour for each test execution. However, because chained method calls rely on multiple inlined calls, all of the intermediate objects of the chain need to be mocked in order to properly define the behaviour of the chained method call within the test execution environment.

Usually, the need for mocking chained statements occurs when dealing with legacy code that you have no control over (ideally, such method call chains should not be implemented if you’re following best development practices, as clearly stated in Mockito’s documentation):

WARNING: This feature should rarely be required for regular clean code! Leave it for legacy code. Mocking a mock to return a mock, to return a mock, (…), to return something meaningful hints at violation of Law of Demeter or mocking a value object (a well known anti-pattern).

As title of example, let’s take a look at the service presented in the previous example now following a chained method call approach with a certain twist in the architecture in which each sub-service relies on an external dedicated cost service to compute the appropriate repair cost.

Note: this architecture is defected on purpose as so to demonstrate how to overcome the mocking cascade due to chained method calls. Ideally, it should be responsibility of each service to compute the cost and not to expose an instance of its dependency to its clients.

Chained Method Calls Example — Deformed Garage Service (repo link)

From the above snippet, it is clearly noticeable the higher number of both mocking statements and resources. With this approach, we need to not only mock the dependency of the unit but also the returned object instances of the dependency’s interface!

This directly impacts both the readability/understanding of the mocking intention as well as the maintainability of the unit test class due to the higher number of statements and resources to handle.

Let’s see how we can improve the readability and understanding of our test code with the help of a special Mockito feature.

Mockito’s Deep Stub Return (@RETURNS_DEEP_STUBS)

In case you actually need to mock chained method calls and refactoring of the code is unfeasible, Mockito provides a special annotation known as @RETURNS_DEEP_STUBS which prevents the high number of cascade mocking statements by abstracting such need within its internals and thus assuming directly the return value for the specified chained method call to mock.

The code snippet below presents the above unit test now refactored with the use of the@RETURNS_DEEP_STUBS annotation.

Refactored Unit Test with @RETURNS_DEEP_STUBS— Garage Service (repo link)

It is clear that the number of statements is significantly reduced when compared to the previous approach. In fact, it is now more readable and understandable what is the responsibility of mocking statement. There is also no longer the need of maintaining the mock instances of the intermediate objects of the chain as these are abstracted through the use of the annotation.

As it can be concluded, the test code is significantly more readable and maintainable without the need of multiple mock-specific statements and resources for intermediate chained method calls objects.

Conclusion

This article introduced the mock chaining problem and how this affects both readability and maintainability of a given codebase. It was also presented a Mockito’s solution to avoid an ever growing cascade of mocking statements due to method call chaining (@RETURNS_DEEP_STUBS annotation) and how this greatly improves unit test code readability without the need of ever-growing mocking statements.

Please feel free to go over the complete application’s code for implementation details. I took the liberty of splitting the various examples provided through the article in different branches for better understanding of the code.

I hope this article was informative and that it can help to keep your applications more readable and maintainable. Feel free to let me know how it worked out for you :) Thank you and be tuned for further articles!

--

--

Francisco Maria
Francisco Maria

No responses yet