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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath /> </parent> <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> <properties> <java.version>17</java.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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <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> </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;
@Repositorypublic 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;
@Servicepublic 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;
@SpringBootApplicationpublic 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.runner.RunWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.Mockito;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); }}
Run: Unit test + Code coverage
Step 1: Install the EclEmma plugin from Eclipse Marketplace Client
Step 2: Right-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
More related topics,
Java - Angular - VueJS - ReactJS