Java Unit Testing with Mockito Example
In this post, we feature a comprehensive article about Java Unit Testing with Mockito Example.
1. Introduction
Java unit testing is a software testing which focuses on testing methods in a class. When a class depends on other classes and/or interfaces, we can test it by using Mockito to create and configure mock objects.
In this example, first, I will create a class which depends on an interface and other class. Second, I will test it using Mockito with Junit framework. I will demonstrate the following actions:
- Define a mocked object with
@Mock. - Define a test class which depends on mocked objects with
@InjectMocks. - Configure a mocked object’s behavior.
- Verify a mocked object’s behavior.
2. Technologies Used
The example code in this article was built and run using:
- Java 11
- Maven 3.3.9
- Eclipse Oxygen
- Junit 4.12 and 5.5.2
3. Maven Project
In this step, I will demonstrate how to use Mockito in both JUnit 4 and JUnit 5 in a three-module Maven project:
common– includes four classes –SomeClass,OtherService,SomeInterface, andSomeException.SomeClassdepends onOtherServiceandSomeInterface.JUnit4-demo– testsSomeClasswith Mockito in JUnit 4.JUnit5-demo– testsSomeClasswith Mockito in JUnit 5.
3.1 Dependencies
Parent pom.xml includes three modules.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jcg.zheng.demo</groupId>
<artifactId>junit-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>junit-demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<description>parent project for junit demo</description>
<modules>
<module>common</module>
<module>junit4-demo</module>
<module>junit5-demo</module>
</modules>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.8.2</version>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
</plugins>
</reporting>
</project>4. Common Module
In this step, I will create a common module which contains four classes:
OtherService– a class which has three public methods.SomeInterface– an interface which defines two public methods.SomeException– a run time exceptionSomeClass– has four methods and depends on bothOtherServiceandSomeInterface.
4.1 POM
The common module’s pom.xml is defined as the following:
pom.xml
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>jcg.zheng.demo</groupId> <artifactId>junit-demo</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>common</artifactId> <name>comon</name> <url>http://maven.apache.org</url> </project>
4.2 SomeInterface
In this step, I will create SomeInterface which has the following methods:
doSomething– it does not return anything.getSomeList– it returns a list of string values.
SomeInterface.java
package jcg.zheng.demo;
import java.util.List;
public interface SomeInterface {
void doSomething(String input);
List<String> getSomeList(String input);
}
4.3 OtherService
In this step, I will create OtherService.
OtherService.java
package jcg.zheng.demo;
public class OtherService {
public int calculateByDoubleIt(int num) {
return num * 2;
}
public void doSomething(String input) {
if ("Ok".equalsIgnoreCase(input)) {
System.out.println("do something.");
} else {
throw new RuntimeException("Remote service failed");
}
}
public boolean returnABoolean(String inputData) {
if ("Save".equalsIgnoreCase(inputData)) {
return true;
} else {
return false;
}
}
}
4.4 SomeException
In this step, I will create a SomeException class which extends from RuntimeExcpetion.
SomeException.java
package jcg.zheng.demo;
public class SomeException extends RuntimeException {
private static final long serialVersionUID = 347808963459470775L;
public SomeException(String msg) {
super(msg);
}
}
4.5 SomeClass
In this step, I will create a SomeClass which depends on both SomeInterface and OtherService. It has the following methods:
doubleANumber– returns an integer after invokingsomeService.calculateByDoubleIt.processData– returnsvoid. It depends onsomeInterface.getSomeListandsomeService.doSomethingreturnABoolean– returns aBooleanvalue after callingsomeService.returnABooleanvoidFoo– returnsvoid. It depends onsomeInterface.doSomethingandsomeService.doSomething.
SomeClass.java
package jcg.zheng.demo;
import java.util.List;
public class SomeClass {
private SomeInterface someInterface;
private OtherService someService;
public int doubleANumber(int num) {
try {
return someService.calculateByDoubleIt(num);
} catch (Exception e) {
throw new SomeException("External Service-calculateByDoubleIt failed " + e.getMessage());
}
}
public void processData(String input) {
List<String> rawList = someInterface.getSomeList(input);
if (rawList != null) {
for (String item : rawList) {
someService.doSomething(item);
}
}
}
public boolean returnABoolean(String inputData) {
try {
return someService.returnABoolean(inputData);
} catch (Exception e) {
throw new SomeException("External Service-returnABoolean failed " + e.getMessage());
}
}
public void voidFoo(String inputData) {
try {
if ("Interface".equalsIgnoreCase(inputData)) {
someInterface.doSomething(inputData);
}
someService.doSomething(inputData);
} catch (Exception e) {
throw new SomeException("External Service-doSomething failed " + e.getMessage());
}
}
}
5. JUnit 4 Test
In this step, I will create a JUnit 4 test class to test SomeClass with mocked SomeInterface and OtherService.
5.1 POM
The JUnit4-demo module’s pom.xml and depends on mockito-core and the common module.
pom.xml
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jcg.zheng.demo</groupId>
<artifactId>junit-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>junit4-demo</artifactId>
<name>junit4-demo</name>
<url>http://maven.apache.org</url>
<properties>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>jcg.zheng.demo</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
5.2 SomeClassTest
In this step, I will create a SomeClassTest class in JUnit 4. It will use the following Mockito annotation and libraries:
- @Mock – marks a field as a mock object.
- @InjectMocks – marks a field on which injection should be performed.
- @RunWith(MockitoJUnitRunner.class) – Integrates with Junit 4 runner
- Mockito static methods:
when,doThrow,times, andverify.
SomeClassTest.java
package jcg.zheng.demo.junit4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import jcg.zheng.demo.SomeException;
import jcg.zheng.demo.SomeClass;
import jcg.zheng.demo.OtherService;
import jcg.zheng.demo.SomeInterface;
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {
@InjectMocks
private SomeClass classUnderTest;
@Mock
private SomeInterface mockedInterface;
@Mock
private OtherService mockedService;
@Test
public void test_doubleANumber() {
when(mockedService.calculateByDoubleIt(3)).thenReturn(6);
assertEquals(6, classUnderTest.doubleANumber(3));
verify(mockedService, times(1)).calculateByDoubleIt(3);
}
@Test(expected = SomeException.class)
public void test_doubleANumber_exception() {
when(mockedService.calculateByDoubleIt(3)).thenThrow(NullPointerException.class);
classUnderTest.doubleANumber(3);
verify(mockedService, times(1)).calculateByDoubleIt(3);
}
@Test
public void test_processData() {
when(mockedInterface.getSomeList(anyString())).thenReturn(null);
classUnderTest.processData("NA");
verify(mockedInterface, times(1)).getSomeList(anyString());
verify(mockedService, times(0)).doSomething(anyString());
}
@Test
public void test_processData_2() {
List<String> twoItemsList = Arrays.asList("Mary", "Zheng");
when(mockedInterface.getSomeList(anyString())).thenReturn(twoItemsList);
classUnderTest.processData("NA");
verify(mockedInterface, times(1)).getSomeList(anyString());
verify(mockedService, times(2)).doSomething(anyString());
}
@Test(expected = SomeException.class)
public void test_returnBooleanFoo_exception() {
when(mockedService.returnABoolean("NA")).thenThrow(NullPointerException.class);
classUnderTest.returnABoolean("NA");
verify(mockedService, times(1)).returnABoolean("NA");
}
@Test
public void test_returnBooleanFoo_false() {
when(mockedService.returnABoolean("NA")).thenReturn(false);
boolean shouldReturnFalse = classUnderTest.returnABoolean("NA");
assertFalse(shouldReturnFalse);
}
@Test
public void test_returnBooleanFoo_true() {
when(mockedService.returnABoolean("Save")).thenReturn(true);
boolean shouldReturnTrue = classUnderTest.returnABoolean("Save");
assertTrue(shouldReturnTrue);
verify(mockedService, times(1)).returnABoolean("Save");
}
@Test
public void test_voidFoo() throws IllegalAccessException {
try {
classUnderTest.voidFoo("Ok");
verify(mockedService, times(1)).doSomething("Ok");
verify(mockedInterface, times(0)).doSomething(anyString());
} catch (Exception e) {
fail("Should not throw exception");
}
}
@Test(expected = SomeException.class)
public void test_voidFoo_exception() {
doThrow(IllegalStateException.class).when(mockedService).doSomething("NA");
classUnderTest.voidFoo("NA");
}
@Test
public void test_voidFoo_interface() throws IllegalAccessException {
try {
classUnderTest.voidFoo("Interface");
verify(mockedService, times(1)).doSomething("Interface");
verify(mockedInterface, times(1)).doSomething("Interface");
} catch (Exception e) {
fail("Should not throw exception");
}
}
}
Output
[INFO] Running jcg.zheng.demo.junit4.SomeClassTest [INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.43 s - in jcg.zheng.demo.junit4.SomeClassTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
6. JUnit 5 Module
In this step, I will create a JUnit 5 test class to test SomeClass with mocked objects.
6.1 POM
The JUnit5-demo module’s pom.xml depends on JUnit 5 and common modules. Please note that it includes: junit-jupiter-engine ,junit-jupiter-api, mockito-core, and mockito-junit-jupiter.
pom.xml
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jcg.zheng.demo</groupId>
<artifactId>junit-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>junit5-demo</artifactId>
<name>junit5-demo</name>
<url>http://maven.apache.org</url>
<properties>
<junit-jupiter.version>5.5.2</junit-jupiter.version>
</properties>
<dependencies>
<dependency>
<groupId>jcg.zheng.demo</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>2.23.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
6.2 SomeClassTest
In this step, I will create a SomeClassTest class in JUnit 5. It has the same annotations as Junit 4, the only difference is Junit 5 uses @ExtendWith(MockitoExtension.class).
SomeClassTest.java
package jcg.zheng.demo.junit5;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import jcg.zheng.demo.SomeException;
import jcg.zheng.demo.SomeClass;
import jcg.zheng.demo.OtherService;
import jcg.zheng.demo.SomeInterface;
@ExtendWith(MockitoExtension.class)
public class SomeClassTest {
@InjectMocks
private SomeClass classUnderTest;
@Mock
private SomeInterface mockedInterface;
@Mock
private OtherService mockedService;
@Test
@DisplayName("It should double a number")
public void test_doubleANumber() {
when(mockedService.calculateByDoubleIt(3)).thenReturn(6);
assertEquals(6, classUnderTest.doubleANumber(3));
verify(mockedService, times(1)).calculateByDoubleIt(3);
}
@Test
public void test_doubleANumber_exception() {
when(mockedService.calculateByDoubleIt(3)).thenThrow(NullPointerException.class);
assertThrows(SomeException.class, () -> {
classUnderTest.doubleANumber(3);
});
verify(mockedService, times(1)).calculateByDoubleIt(3);
}
@Test
public void test_processData() {
when(mockedInterface.getSomeList(anyString())).thenReturn(null);
classUnderTest.processData("NA");
verify(mockedInterface, times(1)).getSomeList(anyString());
verify(mockedService, times(0)).doSomething(anyString());
}
@Test
public void test_processData_2() {
List<String> twoItemsList = Arrays.asList("Mary", "Zheng");
when(mockedInterface.getSomeList(anyString())).thenReturn(twoItemsList);
classUnderTest.processData("NA");
verify(mockedInterface, times(1)).getSomeList(anyString());
verify(mockedService, times(2)).doSomething(anyString());
}
@Test
public void test_returnBooleanFoo_exception() {
when(mockedService.returnABoolean("NA")).thenThrow(NullPointerException.class);
assertThrows(SomeException.class, () -> {
classUnderTest.returnABoolean("NA");
});
verify(mockedService, times(1)).returnABoolean("NA");
}
@Test
public void test_returnBooleanFoo_false() {
when(mockedService.returnABoolean("NA")).thenReturn(false);
boolean shouldReturnFalse = classUnderTest.returnABoolean("NA");
assertFalse(shouldReturnFalse);
}
@Test
public void test_returnBooleanFoo_true() {
when(mockedService.returnABoolean("Save")).thenReturn(true);
boolean shouldReturnTrue = classUnderTest.returnABoolean("Save");
assertTrue(shouldReturnTrue);
verify(mockedService, times(1)).returnABoolean("Save");
}
@Test
public void test_voidFoo() throws IllegalAccessException {
classUnderTest.voidFoo("OK");
verify(mockedService, times(1)).doSomething("OK");
}
@Test
public void test_voidFoo_exception() {
doThrow(IllegalStateException.class).when(mockedService).doSomething("NA");
assertThrows(SomeException.class, () -> {
classUnderTest.voidFoo("NA");
});
}
@Test
public void test_voidFoo_interface() throws IllegalAccessException {
classUnderTest.voidFoo("Interface");
verify(mockedService, times(1)).doSomething("Interface");
verify(mockedInterface, times(1)).doSomething("Interface");
}
}
Output
[INFO] Running jcg.zheng.demo.junit5.SomeClassTest [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.042 s - in jcg.zheng.demo.junit5.SomeClassTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
7. Summary
In this example, I demonstrated how to use Mockito to
- Create mocked objects with
@Mock. - Inject the mocked objects for the testing class marked with
@InjectMocks. - Use Mockito
staticmethod to mock the object’s behavior. - Verify the mocked object’s method is invoked.
8. Download the Source Code
You can download the full source code of this example here: Java Unit Testing With Mokito Example
