Spring Boot implements unit testing based on JUint5

This article introduces Spring Boot 2's unit test implementation based on JUnit 5.


table of Contents

  • Introduction
  • Differences between JUnit 4 and JUnit 5
    1. Ignore test case execution
    2. RunWith Configuration
    3. @Before, @BeforeClass, @After, @AfterClassIs replaced
  • Development environment
  • Example

Introduction

Spring Boot 2.2.0 version began to introduce JUnit 5 as the default library for unit testing. Before Spring Boot 2.2.0, it spring-boot-starter-testincluded the dependency of JUnit 4. After Spring Boot 2.2.0, it was replaced with Junit Jupiter.


Differences between JUnit 4 and JUnit 5

1. Ignore test case execution

JUnit 4:

 

@Test
@Ignore
public void testMethod() {
   // ...
}

JUnit 5:

 

@Test
@Disabled("explanation")
public void testMethod() {
   // ...
}

2. RunWithConfiguration

JUnit 4:

 

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
    @Test
    public void contextLoads() {
    }
}

JUnit 5:

 

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ApplicationTests {
    @Test
    public void contextLoads() {
    }
}

3. @Before, @BeforeClass, @After, @AfterClassis replaced

  • @BeforeEach replace @Before
  • @BeforeAll replace @BeforeClass
  • @AfterEach replace @After
  • @AfterAll replace @AfterClass

Development environment

  • JDK 8

Example

  1. Create a Spring Boot project, refer to: IntelliJ IDEA to create a Spring Boot project .

  2. Adding spring-boot-starter-webdependency, eventually pom.xmlfollows.

 

<?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 https://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.2.6.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>tutorial.spring.boot</groupId>
    <artifactId>spring-boot-junit5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-junit5</name>
    <description>Demo project for Spring Boot Unit Test with JUnit 5</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

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

</project>
  1. After the project is created, a test class is automatically generated.

 

package tutorial.spring.boot.junit5;

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

@SpringBootTest
class SpringBootJunit5ApplicationTests {

    @Test
    void contextLoads() {
    }

}

The function of this test class is to check whether the application context can be started normally. @SpringBootTestAnnotation tells Spring Boot find with @SpringBootApplicationthe main configuration class notes, and use the class to start the Spring application context.

  1. Supplement the application logic code to be tested

4.1. Define Service Layer Interface

 

package tutorial.spring.boot.junit5.service;

public interface HelloService {

    String hello(String name);
}

4.2. Define the Controller layer

 

package tutorial.spring.boot.junit5.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import tutorial.spring.boot.junit5.service.HelloService;

@RestController
public class HelloController {

    private final HelloService helloService;

    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @GetMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name) {
        return helloService.hello(name);
    }
}

4.3. Define the Service layer implementation

 

package tutorial.spring.boot.junit5.service.impl;

import org.springframework.stereotype.Service;
import tutorial.spring.boot.junit5.service.HelloService;

@Service
public class HelloServiceImpl implements HelloService {

    @Override
    public String hello(String name) {
        return "Hello, " + name;
    }
}
  1. Write unit tests that send HTTP requests.

 

package tutorial.spring.boot.junit5;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HttpRequestTest {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testHello() {
        String requestResult = this.restTemplate.getForObject("http://127.0.0.1:" + port + "/hello/spring",
                String.class);
        Assertions.assertThat(requestResult).contains("Hello, spring");
    }
}

Description:

  • webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT Use a local random port to start the service;
  • @LocalServerPortEquivalent @Value("${local.server.port}");
  • In the configuration webEnvironmentafter, Spring Boot automatically provides an TestRestTemplateexample, may be used to send an HTTP request.
  • Except TestRestTemplateHTTP request example, the means may also org.springframework.test.web.servlet.MockMvcaccomplish similar functions, as follows:

 

package tutorial.spring.boot.junit5.controller;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private HelloController helloController;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testNotNull() {
        Assertions.assertThat(helloController).isNotNull();
    }

    @Test
    public void testHello() throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring"))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Hello, spring"));
    }
}

The above test method belongs to the overall test, that is, all application contexts are activated. There is also a layered test method, such as testing only the Controller layer.

  1. Layered testing.

 

package tutorial.spring.boot.junit5.controller;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
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.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import tutorial.spring.boot.junit5.service.HelloService;

@WebMvcTest
public class HelloControllerTest {

    @Autowired
    private HelloController helloController;

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private HelloService helloService;

    @Test
    public void testNotNull() {
        Assertions.assertThat(helloController).isNotNull();
    }

    @Test
    public void testHello() throws Exception {
        Mockito.when(helloService.hello(Mockito.anyString())).thenReturn("Mock hello");
        this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring"))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("Mock hello"));
    }
}

Description:

  • @WebMvcTestNotes tell Spring Boot only instantiated Controller layer, not to instantiate the overall context, and also further specify only instantiate an instance of the Controller layer: @WebMvcTest(HelloController.class);
  • Because the only example of the Controller layer, so dependent Service layer instances need to @MockBeancreate, and by Mockitothe method specified return results when Mock out of the Service layer instance method call in a particular situation.

Guess you like

Origin blog.csdn.net/suifeng629/article/details/107120186