DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Creating Your Swiss Army Knife on Java Test Stack
  • Comprehensive Guide to Property-Based Testing in Go: Principles and Implementation
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • Solid Testing Strategies for Salesforce Releases

Trending

  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  • Measuring the Impact of AI on Software Engineering Productivity
  • Start Coding With Google Cloud Workstations
  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Demystifying Static Mocking With Mockito

Demystifying Static Mocking With Mockito

Many times, we have to deal with a situation when our code invokes a static method. Discover a possible solution for mocking static methods with Mockito.

By 
Arnošt Havelka user avatar
Arnošt Havelka
DZone Core CORE ·
Nov. 28, 23 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
14.6K Views

Join the DZone community and get the full member experience.

Join For Free

These days, writing tests is a standard part of development. Unfortunately, we need to deal from time to time with a situation when a static method is invoked by a tested component. Our goal is to mitigate this part and avoid third-party component behavior. This article sheds light on the mocking of static methods by using the "inline mock maker" introduced by Mockito in the 3.4 version. In other words, this article explains Mockito.mockStatic method in order to help us with unwanted invocation of the static method.

In This Article, You Will Learn

  • How to mock and verify static methods with mockStatic feature
  • How to setup mockStatic in different Mockito versions

Introduction

Many times, we have to deal with a situation when our code invokes a static method. It can be our own code (e.g., some utility class or class from a third-party library). The main concern in unit testing is to focus on the tested component and ignore the behavior of any other component (including static methods). An example is when a tested method in component A is calling an unrelated static method from component B.

Even so, it's not recommended to use static methods; we see them a lot (e.g., utility classes). The reasoning for avoiding the usage of static methods is summarized very well in Mocking Static Methods With Mockito.

Generally speaking, some might say that when writing clean object-orientated code, we shouldn’t need to mock static classes. This could typically hint at a design issue or code smell in our application.

Why? First, a class depending on a static method has tight coupling, and second, it nearly always leads to code that is difficult to test. Ideally, a class should not be responsible for obtaining its dependencies, and if possible, they should be externally injected.

So, it’s always worth investigating if we can refactor our code to make it more testable. Of course, this is not always possible, and sometimes we need to mock static methods.

A Simple Utility Class

Let's define a simple SequenceGenerator utility class used in this article as a target for our tests. This class has two "dumb" static methods (there's nothing fancy about them). The first nextId method (lines 10-12) generates a new ID with each invocation, and the second nextMultipleIds method (lines 14-20) generates multiple IDs as requested by the passed argument.

Java
 
@UtilityClass
public class SequenceGenerator {

	private static AtomicInteger counter;

	static {
		counter = new AtomicInteger(1);
	}

	public static int nextId() {
		return counter.getAndIncrement();
	}

	public static List<Integer> nextMultipleIds(int count) {
		var newValues = new ArrayList<Integer>(count);
		for (int i = 0; i < count; i++) {
			newValues.add(counter.getAndIncrement());
		}
		return newValues;
	}

}


MockedStatic Object

In order to be able to mock static methods, we need to wrap the impacted class by "inline mock maker." The mocking of static methods from our  SequenceGenerator class introduced above is achievable by MockedStatic instance retrieved via Mockito.mockStatic method. This can be done as:

Java
 
try (MockedStatic<SequenceGenerator> seqGeneratorMock = mockStatic(SequenceGenerator.class)) {
    ...
}


Or

Java
 
MockedStatic<SequenceGenerator> seqGeneratorMock = mockStatic(SequenceGenerator.class));
...
seqGeneratorMock.close();


The created mockStatic instance has to be always closed. Otherwise, we risk ugly side effects in next tests running in the same thread when the same static method is involved (i.e., SequenceGenerator in our case). Therefore, the first option seems better, and it is used in most articles on this topic. The explanation can be found on the JavaDoc site (chapter 48) as:

When using the inline mock maker, it is possible to mock static method invocations within the current thread and a user-defined scope. This way, Mockito assures that concurrently and sequentially running tests do not interfere. To make sure a static mock remains temporary, it is recommended to define the scope within a try-with-resources construct.

To learn more about this topic, check out these useful links:

  • The official site,
  • JavaDoc site and
  • GitHub repository.

Mock Method Invocation

Static methods (e.g., our nextId or nextMultipleIds methods defined above) can be mocked with MockedStatic.when. This method accepts a functional interface defined by MockedStatic.Verification. There are two cases we can deal with.

Mocked Method With No Argument

The simplest case is mocking a static method with no argument (nextId method in our case).  In this case, it's sufficient to pass to seqGeneratorMock.when method only a method reference (see line 5). The returned value is specified in a standard way (e.g., with thenReturn method).

Java
 
@Test
void whenWithoutArgument() {
	try (MockedStatic<SequenceGenerator> seqGeneratorMock = mockStatic(SequenceGenerator.class)) {
		int newValue = 5;
		seqGeneratorMock.when(SequenceGenerator::nextId).thenReturn(newValue);

		assertThat(SequenceGenerator.nextId()).isEqualTo(newValue);
	}
}


Mocked Method With One or More Arguments

Usually, we have a static method with some arguments (nextMultipleIds in our case). Then, we need to use a lambda expression instead of the method reference (see line 5). Again, we can use the standard methods (e.g. then, thenRetun, thenThrow etc.) to handle the response with the desired behavior.

Java
 
@Test
void whenWithArgument() {
	try (MockedStatic<SequenceGenerator> seqGeneratorMock = mockStatic(SequenceGenerator.class)) {
		int newValuesCount = 5;
		seqGeneratorMock.when(() -> SequenceGenerator.nextMultipleIds(newValuesCount))
				.thenReturn(List.of(1, 2, 3, 4, 5));

		assertThat(SequenceGenerator.nextMultipleIds(newValuesCount)).hasSize(newValuesCount);
	}
}


Verify Method Invocation

Similarly, we can also verify calls of the mocked component by calling seqGeneratorMock.verify method for the method reference (see line 7)

Java
 
@Test
void verifyUsageWithoutArgument() {
	try (MockedStatic<SequenceGenerator> seqGeneratorMock = mockStatic(SequenceGenerator.class)) {
		
		var person = new Person("Pamela");

		seqGeneratorMock.verify(SequenceGenerator::nextId);
		assertThat(person.getId()).isEqualTo(0);
	}
}


Or the lambda expression (see line 6).

Java
 
@Test
void verifyUsageWithArgument() {
	try (MockedStatic<SequenceGenerator> seqGeneratorMock = mockStatic(SequenceGenerator.class)) {
		List<Integer> nextIds = SequenceGenerator.nextMultipleIds(3);
		
		seqGeneratorMock.verify(() -> SequenceGenerator.nextMultipleIds(ArgumentMatchers.anyInt()));
		assertThat(nextIds).isEmpty();
	}
}


Note: please be aware that seqGeneratorMock doesn't provide any value here, as the static methods are still mocked with the defaults. There's no spy version so far. Therefore, any expected return value has to be mocked, or the default value is returned.

Setup

The mockStatic feature is enabled in Mockito 5.x by default. Therefore, no special setup is needed. But we need to set up Mockito for the older versions (e.g., 4.x).

Mockito 5.x+

As it was already mentioned, we don't need to set up anything in version 5.x. See the statement in the GitHub repository:

Mockito 5 switches the default mockmaker to mockito-inline, and now requires Java 11.

Old Mockito Versions

When an older version is used, and we use the mock-inline feature via mockStatic then we can see an error like this:

Plain Text
 
org.mockito.exceptions.base.MockitoException: 
The used MockMaker SubclassByteBuddyMockMaker does not support the creation of static mocks

Mockito's inline mock maker supports static mocks based on the Instrumentation API.
You can simply enable this mock mode, by placing the 'mockito-inline' artifact where you are currently using 'mockito-core'.
Note that Mockito's inline mock maker is not supported on Android.
	at com.github.aha.poc.junit.person.StaticUsageTests.mockStaticNoArgValue(StaticUsageTests.java:15)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)


Generally, there are two options to enable it for such Mockito versions (see all Mockito versions here).

Use MockMaker Resource

The first option is based on adding <project>\src\test\resources\mockito-extensions\org.mockito.plugins.MockMaker to our Maven project with this content:

Plain Text
 
mock-maker-inline


Use mock-inline Dependency

The other, and probably better, option is adding mockito-inline dependency:

XML
 
<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-inline</artifactId>
	<version>5.2.0</version>
    <scope>test</scope>
</dependency>


Note: this dependency already contains MockMaker resource mentioned above. Therefore, this option seems more convenient.

Maven Warning

No matter what version is used (see above), Maven build can produce these warnings:

Plain Text
 
WARNING: A Java agent has been loaded dynamically (<user_profile>\.m2\repository\net\bytebuddy\byte-buddy-agent\1.12.9\byte-buddy-agent-1.12.9.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended


Mockito works correctly even with these warnings. It's probably caused by/depends on the used tool, JDK version, etc.

Conclusion

In this article, the mocking of static methods with the help of Mockito inline mock maker was covered. The article started with the basics of static mocking and then followed with a demonstration of when and verify usage (either with the method reference or the lambda expression). In the end, the setup of the Mockito inline maker was shown for different Mockito versions.

The used source code can be found here.

Mockito Testing

Opinions expressed by DZone contributors are their own.

Related

  • Creating Your Swiss Army Knife on Java Test Stack
  • Comprehensive Guide to Property-Based Testing in Go: Principles and Implementation
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • Solid Testing Strategies for Salesforce Releases

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!