Spring Boot 2 + JUnit 5 REST API Unit Testing


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.

Advertisements

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.

Advertisements

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.

  1. Annotate the test method with @Test from org.junit.jupiter.api.Test
  2. Prepare the test data and define the mock behavior
  3. Call the REST API using MockMvc
  4. 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());
    }
Advertisements

Source Code

The complete source code is available on Github.