Spring Boot MockMvc 之 单元测试异步休息控制器

学习使用MockMVC 测试Spring Boot异步REST 控制器,该控制器支持异步请求处理并异步返回响应。

1. Spring Boot异步控制器

给定一个异步控制器,该控制器在5秒钟的延迟后返回一个简单的字符串输出。我们将为此控制器编写单元测试。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;

import org.springframework.core.task.TaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldCompletableFutureController 
{
      private final TaskExecutor taskExecutor;

      public HelloWorldCompletableFutureController(TaskExecutor taskExecutor) 
      {
          this.taskExecutor = taskExecutor;
      }

      @GetMapping(value = "/testCompletableFuture")
      public CompletableFuture<String> echoHelloWorld2() 
      {
            return CompletableFuture.supplyAsync(() -> 
            {
                  randomDelay();
                  return "Hello World !!";
            }, taskExecutor);
      }

      private void randomDelay() 
      {
            try 
            {
                  Thread.sleep(ThreadLocalRandom.current().nextInt(5000));
            } 
            catch (InterruptedException e) 
            {
                  Thread.currentThread().interrupt();
            }
      }
}

2.如何使用MockMvc测试异步Rest Controller

与常规控制器一样,Spring WebMVC Test框架可用于测试异步控制器

创建一个测试类,并使用@WebMvcTest@RunWith(SpringRunner.class)对其进行注解。在@WebMvcTest将引导包含检测控制器所需要的东西最少的Spring Boot 应用程序。它会自动配置Spring MockMvc,它会自动连接到测试中。

请注意,要测试异步控制器,需要使用asyncDispatch()方法启动异步调度。

下面给出的测试执行以下操作:

  • 执行初始请求并验证其是否启动异步功能。
  • asyncDispatch()应用并发送请求。
  • 最后,我们声明预期的响应。
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

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.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;

import com.how2codex.springasyncexample.web.controller.HelloWorldCompletableFutureController;

@RunWith(SpringRunner.class)
@WebMvcTest(HelloWorldCompletableFutureController.class)
public class HelloWorldCompletableFutureControllerTest 
{

      @Autowired
      private MockMvc mockMvc;

      @Test
      public void testHelloWorldController() throws Exception 
      {
            MvcResult mvcResult = mockMvc.perform(get("/testCompletableFuture"))
                              .andExpect(request().asyncStarted())
                              .andDo(MockMvcResultHandlers.log())
                              .andReturn();
            
            mockMvc.perform(asyncDispatch(mvcResult))
                        .andExpect(status().isOk())
                        .andExpect(content().contentTypeCompatibleWith("text/plain"))
                        .andExpect(content().string("Hello World !!"));
      }
}

3.源代码文件

引导应用程序并配置异步行为的应用程序类。要覆盖默认的异步行为(例如线程池和超时),您可以实现WebMvcConfigurer接口并覆盖其configureAsyncSupport()方法。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class SpringAsyncExampleApplication implements WebMvcConfigurer {

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

      @Override
      public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
            configurer.setTaskExecutor(mvcTaskExecutor());
            configurer.setDefaultTimeout(30_000);
      }

      @Bean
      public ThreadPoolTaskExecutor mvcTaskExecutor() {
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.setThreadNamePrefix("mvc-task-");
            return taskExecutor;
      }
}

3.1。SpringAsyncExampleApplication.java

3.2。application.properties

在此处启用调试日志记录以了解应用程序的行为。

logging.level.org.springframework=DEBUG
logging.level.com.how2codex=DEBUG

3.3。pom.xml

使用的pom.xml是:

<?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.1.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
      </parent>
      
      <groupId>com.how2codex.demo</groupId>
      <artifactId>spring-async-demo</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>spring-async-demo</name>
      <description>Demo project for Spring Boot</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-webflux</artifactId>
            </dependency>

            <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
            </dependency>
            <dependency>
                  <groupId>io.projectreactor</groupId>
                  <artifactId>reactor-test</artifactId>
                  <scope>test</scope>
            </dependency>
      </dependencies>

      <build>
            <plugins>
                  <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                  </plugin>
            </plugins>
      </build>
      
      <repositories>
        <repository>
            <id>repository.spring.release</id>
            <name>Spring GA Repository</name>
            <url>http://repo.spring.io/release</url>
        </repository>
    </repositories>

</project>

让我知道您在测试异步spring boot rest控制器时是否遇到任何错误。

学习愉快!

saigon has written 1440 articles

Leave a Reply