Spring Boot + Mockito simple application with 100% code coverage

In this article, we will show you a simple Spring Boot example to demonstrate test methods for Controllers, Service, and Repository, And code coverage analysis using the EclEmma plugin.



Technologies used:

  • Spring Boot 2.6.4
  • Mockito 3.11.2
  • Maven 3+
  • EclEmma plugin
  • Junit 5
  • Java 17
A quick overview of  Spring Boot, Mockito, and EclEmma plugin


Spring boot:

Spring boot to develop REST web services and microservices. Spring Boot has taken the Spring framework to the next level. It has drastically reduced the configuration and setup time required for spring projects. We can set up a project with almost zero configuration and start building the things that actually matter to your application.


Mockito:

Mockito is a mocking framework, a JAVA-predicated library that is utilized for efficacious unit testing of JAVA applications. Mockito is utilized to mock interfaces so that a dummy functionality can be integrated into a mock interface that can be utilized in unit testing.


EclEmma:

EclEmma is a free Java code coverage implement for Eclipse, available under the Eclipse Public License. It brings code coverage analysis directly into the Eclipse workbench.


Code Coverage:

Code coverage is the percentage of code that is covered by automated tests. Code coverage quantification simply determines which statements in a body of code have been executed through a test run, and which statements expressions have not.


Let see the following Spring boot MVC web application, and how to perform the unit test with Mockito framework and Junit 4, and code coverage with the EclEmma plugin.


Project Structure:




Maven[pom.xml]

A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project. It contains default values for most projects. Some of the configurations that can be designated in the POM is the project dependencies, the plugins or goals that can be executed, the build profiles, and so on. Other information such as the project version, description, developers, mailing lists, and such can withal be designated.
<?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>com.knf.dev</groupId>
<artifactId>spring-boot-mockito-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-mockito-example</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath />
</parent>

<properties>
<java.version>17</java.version>
<junit-jupiter.version>5.3.2
</junit-jupiter.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>

<!-- exclude junit 4 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.2</version>
</dependency>
<!-- junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>

</plugins>
</build>

</project>


Entity class[User.java]
package com.knf.dev.mockito.entity;

import javax.persistence.*;

@Entity
@Table(name = "user")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;

private String name;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys. 


Repository layer[UserRepository.java]
package com.knf.dev.mockito.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.knf.dev.mockito.entity.User;

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

public User findByName(String name);

}
@Repository annotation is used to indicate that the class provides the mechanism for storage, retrieval, search, update and delete operation on objects.


Service Layer[UserService.java]
package com.knf.dev.mockito.service;

import java.util.List;

import com.knf.dev.mockito.entity.User;

public interface UserService {

public User getUserByName(String name);
public User saveUser(User user);
public List<User> getAllUsers();
}


UserServiceImpl.java
package com.knf.dev.mockito.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.knf.dev.mockito.entity.User;
import com.knf.dev.mockito.repository.UserRepository;

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepository;

@Override
public User getUserByName(String name) {
return userRepository.findByName(name);
}

@Override
public User saveUser(User user) {
return userRepository.save(user);
}

@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}

}


RestController[UserRestController.java]
package com.knf.dev.mockito.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.knf.dev.mockito.entity.User;
import com.knf.dev.mockito.service.UserService;

@RestController
@RequestMapping("/api")
public class UserRestController {

@Autowired
private UserService userService;

@GetMapping("/user/{username}")
public User getUserByName(@PathVariable String username) {
return userService.getUserByName(username);
}

@PostMapping("/user")
public User saveUser(@RequestBody User user) {
return userService.saveUser(user);
}
@GetMapping("/user")
public List<User> getAllUsers() {
return userService.getAllUsers();
}
}
The @RestController annotation was introduced in Spring 4.0 to simplify the engendering of RESTful web services. It's a convenience annotation that combines @Controller and @ResponseBody. @RequestMapping annotation maps HTTP requests to handler methods of MVC and REST controllers.


Main class[Application.java]
package com.knf.dev.mockito;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}


Unit testing

UserRestControllerTest.java
package com.knf.dev.mockito.controller;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.
servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.
servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.
servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.
autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.
servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.
servlet.result.MockMvcResultMatchers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.knf.dev.mockito.entity.User;
import com.knf.dev.mockito.service.UserService;



@RunWith(SpringRunner.class)
@WebMvcTest(UserRestController.class)
public class UserRestControllerTest {

@Autowired
private MockMvc mvc;

@MockBean
private UserService userService;

@Test
public void getUserByName() throws Exception {

User user = new User();
user.setName("jadu");
user.setId(4);
when(userService.getUserByName("jadu")).thenReturn(user);

mvc.perform(get("/api/user/jadu").
contentType(MediaType.APPLICATION_JSON)).
andExpect(status().isOk())
.andExpect(jsonPath("$.name").
value("jadu"));

}

@Test
public void saveUSer() throws Exception {

User user = new User();
user.setName("jadu");
user.setId(4);

when(userService.saveUser(user)).thenReturn(user);

mvc.perform(MockMvcRequestBuilders.post("/api/user").
content(asJsonString(user))
.contentType(MediaType.APPLICATION_JSON).
accept(MediaType.APPLICATION_JSON)).
andExpect(status().isOk());
}

@Test
public void getAllUsers() throws Exception {

User user = new User();
user.setName("jadu");
user.setId(4);
User user1 = new User();
user1.setName("jadu1");
user1.setId(5);
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user);
when(userService.getAllUsers()).thenReturn(users);


mvc.perform(get("/api/user").
contentType(MediaType.APPLICATION_JSON)).
andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.
jsonPath("$.length()").value(2));

}
public static String asJsonString(final Object obj)
throws JsonProcessingException {

return new ObjectMapper().writeValueAsString(obj);

}
}


UserServiceImplTest.java
package com.knf.dev.mockito.service;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.junit4.SpringRunner;
import com.knf.dev.mockito.entity.User;
import com.knf.dev.mockito.repository.UserRepository;

@RunWith(SpringRunner.class)
public class UserServiceImplTest {

@InjectMocks
private UserServiceImpl userService;

@Mock
private UserRepository userRepository;

@Before
public void setUp() {

User user = new User();
user.setName("danny boy");
user.setId(1);
User user1 = new User();
user1.setName("alpha");
user1.setId(2);
Mockito.when(userRepository.
findByName(user.getName())).thenReturn(user);
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user);
Mockito.when(userRepository.
findAll()).thenReturn(users);
}

@Test
public void getUserByName() {

String name = "danny boy";
User user = userService.getUserByName(name);
Assert.assertEquals(user.getName(), name);
}

@Test
public void saveUser() {

User user = new User();
user.setId(2);
user.setName("dummy user");
userService.saveUser(user);
verify(userRepository, times(1)).save(user);
}

@Test
public void getAllUsers() {

List<User> users = userService.getAllUsers();
Assert.assertEquals(users.size(), 2);
}
}


MockitoApplicationTests.java
package com.knf.dev.mockito;

import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ApplicationTest {

@Test
public void main() {
Application.main(new String[] {});
}

}


Run: Unit test + Code coverage

Step 1: Install the EclEmma plugin from Eclipse Marketplace Client 


Step 2Right-click on our project[spring_mockito_example] and click coverage As > Junit Test

Step 3: Verify 100 Percent Code Coverage 


          

More related topics,

Java - Angular - VueJS - ReactJS

NodeJS - Angular - VueJS - ReactJS

Python - Flask  - Angular - VueJS - ReactJS

Popular posts from this blog

Spring boot video streaming example-HTML5

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Spring Boot + OpenCSV Export Data to CSV Example

Custom Exception Handling in Quarkus REST API

DataTable-Pagination example with Spring boot, jQuery and ajax

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Node JS mini projects with source code - free download

Spring boot web project free download:User Registration System

Java - Blowfish Encryption and decryption Example