【springboot单元测试,集成测试】

本文介绍一下SpringBoot中的测试方法

集成测试

@SpringBootTest

一个普通的web api

java 复制代码
@RequestMapping
@RestController
public class HelloController {

    @Autowired
    RestTemplate restTemplate;

    @GetMapping(value = "/api/hi")
    public Map<String,Object> hello() {

        String baiduRes = restTemplate.getForObject("https://www.baidu.com", String.class);
        Map<String, Object> res = new HashMap<>();
        res.put("status", "中");
        res.put("msg", baiduRes);
        return res;
    }
}

测试类:

java 复制代码
package xyz.bliu.sptest;


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.client.RestTemplate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
public class ControllerTest02 {

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    MockMvc mockMvc;

    @Test
    public void restTemplateShouldNotNull() {
        assertThat(restTemplate).isNotNull();
    }

    @Test
    public void testGetRequest() throws Exception {

        mockMvc.perform(get("/api/hi"))
               .andExpect(status().isOk())
               .andDo(print());
    }
}

使用mockMvc好处是不会启动真实的web服务

当然你可以使用@SpingBootTest 并且注入一个RestTemplate来做真实的请求

假如希望仅仅测试controller层时, 可以使用另外一个注解

@WebMvcTest

他有一个参数可以指定测试的controller

java 复制代码
package xyz.bliu.sptest;


import org.junit.jupiter.api.Test;
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.web.client.RestTemplate;
import xyz.bliu.sptest.controller.HelloController;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;

@WebMvcTest(controllers = HelloController.class)
public class ControllerTest01 {


    @Autowired
    MockMvc mockMvc;

    @MockBean
    RestTemplate restTemplate;

    @Test
    public void helloShouldOK() throws Exception {

        when(restTemplate.getForObject("https://www.baidu.com", String.class)).thenReturn("haha");

        assertThat(mockMvc).isNotNull();
        mockMvc.perform(get("/api/hi").header("kk", "v1")
                        .header("Content-Type", "application/json"))
                .andDo(print())
                .andExpect(content().contentType("application/json"))
                .andExpect(content().json("{'status':'中', 'msg':'haha'}"));
    }


    @Test
    public void restTemplateShouldBeNull() {
        assertThat(restTemplate).isNull();
    }
}

这样仅会加载指定的controller和一些web层的东西不会加载其他Bean

假如这个controller中依赖其他的bean怎么办呢?

答案是需要使用@MockBean去Mock依赖的行为

例如我这里的处理

java 复制代码
 @MockBean
 RestTemplate restTemplate;

 when(restTemplate.getForObject("https://www.baidu.com", String.class)).thenReturn("haha");

其实就是说当调用restTemplate.getForObject("https://www.baidu.com", String.class)时,方法会返回"haha"


@WebMvcTest VS @SpringBootTest

显然当你只需要测试你的controller接收请求参数或者返回值时你可以使用@WebMvcTest, 因为它不需要加载整个application context, 因此会使你的test更快

然而当需要集成测试时则需要@SpringBootTest
并且他们是不能同时使用的

  • 另外你可能注意到了AssertJ 提供的 assertThat api非常好用,可以流式调用

  • 另外本文测试环境为spring boot 3.x 和 Junit5

    如果你使用是springboot 2.x 你可能还需要 @RunWith(SpringRuner.class) ( junit4)或者 @extendwith(springextension.class) (junit5)

当然你可以使用@SpingBootTest 并且注入一个RestTemplate来做真实的请求

java 复制代码
package xyz.bliu.sptest;

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.server.LocalServerPort;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

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


    @Autowired
    RestTemplate restTemplate;

    @LocalServerPort
    int port;

    @Test
    public void testGet() {
        Map resp = restTemplate.getForObject("http://localhost:"+port+ "/api/hi", Map.class);
        assertThat(resp.get("status").toString()).isEqualTo("中");
    }
}
相关推荐
txxzjmzlh1 小时前
SpringBoot 配置⽂件
java·spring boot·spring
Thomas.Sir7 小时前
第13课:MyBatis-Plus整合SpringBoot4.x,分页、条件查询、逻辑删除实战
java·spring boot
小蒜学长12 小时前
编程语言在线学习平台的设计与实现(代码+数据库+LW)
java·spring boot·后端·编程语言在线学习平台·在线编码
Félix25113 小时前
Nacos学习笔记(SpringBoot/SpringCloud)
java·spring boot·spring
lqbzlll14 小时前
Spring Boot 开启虚拟线程后接口反而变慢了?4 个排查方向和对应修法
java·spring boot·后端
企业数字化笔记14 小时前
业务单据怎样接入流程中心?启动条件、实例关联与失败回退
java·spring boot·mysql
孤狼GPT15 小时前
ChatGPT、Codex工程实战:Spring Boot的SSE一直在推送,为什么经过Nginx后前端才一次性显示?
spring boot·chatgpt·sse·codex·chatgptplus
孤狼GPT15 小时前
ChatGPT、Codex性能实战:MySQL升级到8.0后,SQL没改,为什么接口P99反而翻倍?
spring boot·chatgpt·sql优化·codex·mysql8.0
她说..15 小时前
常见设计模式-观察者模式
java·spring boot·spring·观察者模式·设计模式