This article explains how to unit test REST APIs in Spring Boot 2 using JUnit 5. As we are using Spring Boot 2, we will be using Spring Framework 5. We are going to use MockMvc which will mock the Spring MVC infrastructure without starting a web container.
If you are already aware of Spring Boot REST API Unit Testing with JUnit 4, migrating to JUnit 5 will be easy, because the use of MockMvc and @WebMvcTest remains the same.
Let’s quickly jump on coding.
Technology Stack
- Java 10
- Spring Boot 2.0.3.RELEASE
- Junit 5
- Database: HSQLDB
- IDE: Eclipse Photon / IntelliJ IDEA 2018.1.5
JUnit 5 maven configuration
For JUnit 4 adding the dependency for spring-boot-starter-test was enough. But for Junit 5 you need to add following dependencies:
- spring-boot-starter-test
- junit-jupiter-api
- junit-jupiter-engine
- maven-surefire-plugin
The pom.xml is:
<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.0.3.RELEASE</version> </parent> <groupId>com.bytestree.restful</groupId> <artifactId>spring-restful-service-junit5</artifactId> <version>1.0.0-SNAPSHOT</version> <name>Spring Boot 2 Restful Service Unit Testing with Junit 5</name> <description>Unit testing of Spring boot Restful Services using mockito and Junit 5</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.10</maven.compiler.source> <maven.compiler.target>1.10</maven.compiler.target> <jaxb.api.version>2.3.0</jaxb.api.version> <junit.platform.version>1.2.0</junit.platform.version> </properties> <dependencies> <!-- Spring Boot --> <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>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>${jaxb.api.version}</version> </dependency> <!-- Database --> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> </dependency> <!-- Testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <scope>test</scope> </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> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit.platform.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
Test Class configuration
First of all, check the configuration of the test class of your REST Controller :
@ExtendWith(SpringExtension.class) @WebMvcTest(EmployeeController.class) public class EmployeeControllerTest { @Autowired private MockMvc mockMvc; @MockBean EmployeeService empService; .... }
Something about the annotations:
@ExtendWith(SpringExtension.class): Register the SpringExtension which integrates the Spring TestContext Framework into Junit 5’s Jupiter programming model.
@WebMvcTest: Auto configure Spring MVC infrastructure and MockMvc. It will only scan beans related to Web layer, like @Controller, @ControllerAdvice, WebMvcConfigurer etc. This is useful because we are interested only in web layer when unit testing Controller classes. In our case, we are limiting it to EmployeeController.class.
MockMvc: Provide Spring MVC infrastructure without starting the HTTP Server.
@MockBean: It adds Mockito mocks as a bean in Spring ApplicationContext.
JUnit 5 Test method
Now check how to write JUnit test method. As explained in the earlier article, steps to write unit test cases are similar. The only difference is, we should be using JUnit 5 libraries.
- Annotate the test method with @Test from org.junit.jupiter.api.Test
- Prepare the test data and define the mock behavior
- Call the REST API using MockMvc
- Verify the MvcResult is as expected. We are asserting the result using org.junit.jupiter.api.Assertions class.
And here is the code for JUnit test case for POST operation:
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; ... ... @Test public void testAddEmployee() throws Exception { // prepare data and mock's behaviour Employee empStub = new Employee(1l, "bytes", "tree", "developer", 12000); when(empService.save(any(Employee.class))).thenReturn(empStub); // execute MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URL) .contentType(MediaType.APPLICATION_JSON_UTF8) .accept(MediaType.APPLICATION_JSON_UTF8) .content(TestUtils.objectToJson(empStub))).andReturn(); // verify int status = result.getResponse().getStatus(); assertEquals(HttpStatus.CREATED.value(), status, "Incorrect Response Status"); // verify that service method was called once verify(empService).save(any(Employee.class)); Employee resultEmployee = TestUtils.jsonToObject(result.getResponse().getContentAsString(), Employee.class); assertNotNull(resultEmployee); assertEquals(1l, resultEmployee.getId().longValue()); }
Source Code
The complete source code is available on Github.