java:springboot单元测试spring-boot-starter-test

背景

Java的单元测试可以使用多个框架,其中比较流行的包括:

  • JUnit:JUnit是Java单元测试最常用的框架,它提供了一套丰富的API,可以方便地编写测试用例和测试套件。JUnit 5是JUnit的最新版本,引入了许多新功能和改进。
  • Mockito:Mockito是一个模拟框架,可以模拟对象的行为和状态,以便在单元测试中检查方法的调用和参数。它提供了一组强大的API,可以方便地创建模拟对象和验证方法调用。
  • Spring Test:如果你正在使用Spring框架开发应用程序,那么可以使用Spring Test框架进行单元测试。Spring Test提供了一套完整的测试解决方案,可以方便地测试Spring应用程序的各种方面。

Spring Boot Test的主要特点包括:

  • 快速创建测试环境:通过使用Spring Boot的自动化配置功能,可以快速创建测试环境,无需手动配置。
  • 提供丰富的测试功能:Spring Boot Test提供了丰富的测试功能,包括注解支持、MockMvc、RestTemplate等,可以方便地进行单元测试和集成测试。
  • 集成JUnit和Mockito:Spring Boot Test集成了JUnit和Mockito等测试框架和模拟框架,可以方便地编写测试用例和模拟对象。
  • 简化测试配置:通过使用Spring Boot的自动化配置功能,可以简化测试配置,只需关注测试用例的编写。

Spring Boot Test 包含了这些库

  • JUnit 5:包含兼容JUnit4,Java 应用程序单元测试的事实标准
  • Spring Test 和 SpringBootTest:对Spring Boot应用程序的公共和集成测试支持
  • AssertJ:流式断言库
  • Hamcrest:匹配对象库
  • Mockito:Java 模拟框架
  • JSONassert:JSON 断言库
  • JsonPath: JSON XPath

MockMvc概念

MockMvc是Spring Test模块的一部分,它允许我们对Spring MVC控制器进行单元测试,而无需启动完整的Spring应用上下文。

MockMvc可以模拟HTTP请求和响应,MockMvc就像是一个虚拟的Spring MVC,能够让我们快速测试控制器。

MockMVC的基本步骤

(1) mockMvc.perform执行一个请求。

(2) MockMvcRequestBuilders.get(“XXX”)构造一个请求。

(3) ResultActions.param添加请求传值

(4) ResultActions.accept()设置返回类型

(5) ResultActions.andExpect添加执行完成后的断言。

(6) ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如处使用print()输出整个响应结果信息。

(7) ResultActions.andReturn表示执行完成后返回相应的结果。

实例化

方法一:通过参数指定一组控制器,这样就不需要从上下文获取

mockMvC = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build();

方法二:指定WebApplicationContext 将会从该上下文获得相应的控制器并得到相应的MockMvc

mockMVC=MockMvcBuilders.webAppContextSetup(webApplicationContext).build();

步骤

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

1、双击选中你要测试的类,点击 Navigate
在这里插入图片描述
2、注意点击 Test 的时候鼠标要放在对应的类上
在这里插入图片描述
3、勾选你想要的
在这里插入图片描述
这时候生成的代码是这样的

package com.zhangyu.controller;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class HelloWorldControllerTest {

    @BeforeEach
    void setUp() {
    }

    @AfterEach
    void tearDown() {
    }

    @Test
    void index() {
    }
}

根据上面的 MockMVC的基本步骤 我们编写代码

package com.zhangyu.controller;

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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@AutoConfigureMockMvc
class HelloWorldControllerTest {
	@Autowired
    private MockMvc mvc;

    @Test
    void index() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();
    }
}

稍微解释一下这个几个新的注解:

  • @SpringBootTest:获取启动类,加载配置,寻找主配置启动类(被 @SpringBootApplication 注解的)
  • @RunWith(SpringRunner.class):让JUnit运行Spring的测试环境,获得Spring环境的上下文的支持。
  • @AutoConfigureMockMvc:是Spring Boot提供的注解,它允许在不启动整个Web服务器的情况下测试控制器。确保Spring的上下文被正确加载

JUnit5常用注解及测试

@Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
@ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
@RepeatedTest :表示方法可重复执行,下方会有详细介绍
@DisplayName :为测试类或者测试方法设置展示名称
@BeforeEach :表示在每个单元测试之前执行
@AfterEach :表示在每个单元测试之后执行
@BeforeAll :表示在所有单元测试之前执行
@AfterAll :表示在所有单元测试之后执行
@Tag :表示单元测试类别,类似于JUnit4中的@Categories
@Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
@Timeout :表示测试方法运行如果超过了指定时间将会返回错误
@ExtendWith :为测试类或测试方法提供扩展类引用

MockMvc

public void getAllCategoryTest() throws Exception
{
    String responseString = mockMvc.perform
            (
                    MockMvcRequestBuilders.post("http://127.0.0.1:8888/login")          //请求的url,请求的方法是post
                            //get("/user/showUser2")          //请求的url,请求的方法是get
                            .contentType(MediaType.APPLICATION_FORM_URLENCODED)//发送数据的格式
                            .param("username","hyh")   //添加参数(可以添加多个)
                            .param("password","123")   //添加参数(可以添加多个)
            )
            //.andExpect(status().isOk())    //返回的状态是200
            .andDo(print())         //打印出请求和相应的内容
            .andReturn().getResponse().getContentAsString();   //将相应的数据转换为字符串
    System.out.println("-----返回的json = " + responseString);
}
  1. mockMvc.perform执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
  2. MockMvcRequestBuilders.post(“http://127.0.0.1:8888/login“)构造一个请求
  3. ResultActions.andExpect添加执行完成后的断言
  4. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
  5. ResultActions.andReturn表示执行完成后返回相应的结果。
mockMvc.perform(post("/user").param("name", "admin")) //执行请求
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;


    // 这个方法在每个方法执行之前都会执行一遍
    @Before
    public void setup() {

        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();   //指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc
    }

    @Test
    public void whenUploadSuccess() throws Exception {

        String result = mockMvc
                .perform(fileUpload("/file").file(new MockMultipartFile("file", "test.txt",
                        "multipart/form-data", "hello upload".getBytes("UTF-8"))))
                    .andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
        System.out.println(result);
    }

    @Test
    public void whenQuerySuccess() throws Exception {

        String result = mockMvc
                .perform(get("/user").param("username", "jojo").param("age", "18")
                        .param("ageTo", "60").param("xxx", "yyy")
                        // .param("size", "15")
                        // .param("page", "3")
                        // .param("sort", "age,desc")
                        .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk()).andExpect(jsonPath("$.length()").value(3)).andReturn()
                .getResponse().getContentAsString();

        System.out.println(result);
    }

    @Test
    public void whenGetInfoSuccess() throws Exception {

        String result = mockMvc.perform(get("/user/1").contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk()).andExpect(jsonPath("$.username").value("tom"))
                .andReturn().getResponse().getContentAsString();

        System.out.println(result);
    }

    @Test
    public void whenGetInfoFail() throws Exception {

        mockMvc.perform(get("/user/a").contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().is4xxClientError());
    }

    @Test
    public void whenCreateSuccess() throws Exception {

        Date date = new Date();
        System.out.println(date.getTime());
        String content = "{\"username\":\"tom\",\"password\":\"root\",\"birthday\":" + date.getTime()
                + "}";
        String reuslt = mockMvc
                .perform(
                        post("/user").contentType(MediaType.APPLICATION_JSON_UTF8).content(content))
                .andExpect(status().isOk()).andExpect(jsonPath("$.id").value("1")).andReturn()
                .getResponse().getContentAsString();

        System.out.println(reuslt);
    }

    @Test
    public void whenCreateFail() throws Exception {

        Date date = new Date();
        System.out.println(date.getTime());
        String content = "{\"username\":\"tom\",\"password\":null,\"birthday\":" + date.getTime()
                + "}";
        String reuslt = mockMvc
                .perform(
                        post("/user").contentType(MediaType.APPLICATION_JSON_UTF8).content(content))
                // .andExpect(status().isOk())
                // .andExpect(jsonPath("$.id").value("1"))
                .andReturn().getResponse().getContentAsString();

        System.out.println(reuslt);
    }

    @Test
    public void whenUpdateSuccess() throws Exception {

        Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault())
                .toInstant().toEpochMilli());
        System.out.println(date.getTime());
        String content = "{\"id\":\"1\", \"username\":\"tom\",\"password\":null,\"birthday\":"
                + date.getTime() + "}";
        String reuslt = mockMvc
                .perform(put("/user/1").contentType(MediaType.APPLICATION_JSON_UTF8)
                        .content(content))
                .andExpect(status().isOk()).andExpect(jsonPath("$.id").value("1")).andReturn()
                .getResponse().getContentAsString();

        System.out.println(reuslt);
    }

    @Test
    public void whenDeleteSuccess() throws Exception {

        mockMvc.perform(delete("/user/1").contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk());
    }

}

断言

这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。
在这里插入图片描述

JUnit4 升级 Junit5

在进行迁移的时候需要注意如下的变化:

  • 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
  • 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
  • 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
  • 把@Ignore 替换成@Disabled。
  • 把@Category 替换成@Tag。
  • 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lvan的前端笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值