知识杂货铺 知识杂货铺
首页
后端(1本书)
  • 主题初衷与诞生
  • 介绍
  • 快速上手
  • 目录结构
  • 核心配置和约定
  • 自动生成front matter
  • Markdown 容器
  • Markdown 中使用组件
  • 相关文章

    • 使目录栏支持h2~h6标题
    • 如何让你的笔记更有表现力
    • 批量操作front matter工具
    • 部署
    • 关于写文章和H1标题
    • 关于博客搭建与管理
    • 在线编辑和新增文章的方法
  • 主题配置
  • 首页配置
  • front matter配置
  • 目录页配置
  • 添加摘要
  • 修改主题颜色和样式
  • 评论栏
  • 快速开始
  • 代码集成_TODO
  • 框架初探
  • 在GitHub上贡献代码
  • 使用K8s部署系统
  • Seata分布式事务
GitHub (opens new window)

Kevin Zhang

爱凑热闹的高龄程序猿
首页
后端(1本书)
  • 主题初衷与诞生
  • 介绍
  • 快速上手
  • 目录结构
  • 核心配置和约定
  • 自动生成front matter
  • Markdown 容器
  • Markdown 中使用组件
  • 相关文章

    • 使目录栏支持h2~h6标题
    • 如何让你的笔记更有表现力
    • 批量操作front matter工具
    • 部署
    • 关于写文章和H1标题
    • 关于博客搭建与管理
    • 在线编辑和新增文章的方法
  • 主题配置
  • 首页配置
  • front matter配置
  • 目录页配置
  • 添加摘要
  • 修改主题颜色和样式
  • 评论栏
  • 快速开始
  • 代码集成_TODO
  • 框架初探
  • 在GitHub上贡献代码
  • 使用K8s部署系统
  • Seata分布式事务
GitHub (opens new window)
  • Spring Boot 培训教程
  • Spring Boot介绍

  • 开发环境配置

  • 原理剖析

  • Web开发

  • 数据访问

  • 事务

  • 集成Redis

  • 集成MongoDB

  • 异步消息

  • 异常处理

  • 单元测试与热部署

    • 单元测试与热部署
    • 单元测试
      • 11.1 Spring Boot 中的单元测试
        • 11.1.1 使用单元测试
        • 11.1.2 普通后台代码测试
        • 11.1.3 使用 Mock 测试控制层
        • 11.1.4 运行服务器后测试
    • 热部署
    • 课后作业
  • 安全控制

  • 应用监控

  • 企业级开发

  • 多环境配置与部署

  • 综合示例

  • 前后端分离的vue急速入门

  • Spring Boot配置大全

  • 在Docker中部署Spring Boot应用

  • 开发前后端分离应用

  • 前进到Spring Cloud

  • 规则引擎

  • 流程引擎

  • 后记
  • 后端
  • 单元测试与热部署
Kevin Zhang
2024-10-30
目录

单元测试

# 11.1 Spring Boot 中的单元测试

单元测试是指对软件中的最小可测试单元进行检查和验证,单元测试归属于白盒测试。

这个定义有点抽象,下面举几个单元测试的特性,大家感受一下:

  • 一般是一个函数配几个单元测试;
  • 单元测试不应该依赖外部系统;
  • 单元测试运行速度很快;
  • 单元测试不应该造成测试环境的脏数据;
  • 单元测试可以重复运行。

单元测试使得我们可以放心修改、重构业务代码,而不用担心修改某处代码后带来的副作用。

单元测试可以帮助我们反思模块划分的合理性,如果一个单元测试写得逻辑非常复杂、或者说一个函数复杂到无法写单元测试,那就说明模块的抽象有问题。

单元测试使得系统具备更好的可维护性、具备更好的可读性。对于团队的新人来说,可以从单元测试入手阅读代码,进而熟悉系统的逻辑。

优秀的开源框架,都配有完善的单元测试套件(Test Suite),以保证代码质量。

越是底层的代码,越是被更多客户调用的代码,越应该实践 TDD(Test-Driven Development,测试驱动开发)。

测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。

虽然,我们日常工作中不会为每一段业务逻辑(类或方法)提供单元测试,但是针对大部分的产品级业务系统(主要工作是在某一技术基础平台上实现业务逻辑),都应该写单元测试。

大部分开发人员使用的单元测试框架都是 JUnit,这也是 Spring Boot 的选择。

# 11.1.1 使用单元测试

Spring Boot 官方文档 (opens new window),对如何进行测试进行了充分的说明。

Spring Boot provides a number of utilities and annotations to help when testing your application. Test support is provided by two modules: spring-boot-test contains core items, and spring-boot-test-autoconfigure supports auto-configuration for tests.

使用 Spring Starter 创建项目时,默认就添加了spring-boot-starter-test依赖。

image-20191225223621508

查看项目 pom 文件中的单元测试依赖:

<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>
1
2
3
4
5
6
7
8
9
10
11

spring-boot-starter-test启动器依赖提供如下支持库:

  • JUnit 5(包括与JUnit 4向后兼容的老式引擎):Java 领域的单元测试事实标准,使用最广泛的测试支持库。
  • Spring Test 和 Spring Boot Test:对 Spring Boot 应用提供集成测试支持。
  • AssertJ:Java 领域常用的功能完备的断言库。
  • Hamcrest:一个匹配对象的库(也称为约束或谓词)。
  • Mockito:一个 Java 模拟框架,比如模拟 http 容器环境。
  • JSONassert:JSON 的断言库。
  • JSONPath:JSON 的 XPath 支持库。

Spring Boot 认为这些公共库在编写测试时很有用。如果这些库不适合我们的需要,也可以手动添加自己喜欢的测试依赖库。

通过查看org.springframework.boot:spring-boot-starters依赖,可以看到其依赖的 JUnit 的版本信息

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.5.2</version>
    <scope>compile</scope>
</dependency>
1
2
3
4
5
6

打开 Spring Boot 创建的项目中的默认单元测试入口类SpringBootTestApplicationTests:

package com.example.test;

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

@SpringBootTest
class SpringBootTestApplicationTests {

	@Test
	void contextLoads() {
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13

在 contextLoads 方法中添加一个断言:

@Test
void contextLoads() {
    int num = new Integer(6998);
    assertEquals(num, 6998);
}
1
2
3
4
5

选中单元测试入口类,运行JUnit Test:

image-20191226232522064

查看单元测试运行结果,如果没有错误,会出现熟悉的绿色长条。

image-20191226232651798

# 11.1.2 普通后台代码测试

添加测试用的 DAO 类 TestDAO,模拟从数据库中查询数据后返回用户年龄。其业务逻辑是“kevin用户的年龄为18岁”。

package com.example.test.dao;

import org.springframework.stereotype.Repository;

@Repository
public class TestDAO {

	//模拟从数据库访问数据后返回用户的年龄
	public int getUserAge(String userId) {
		int result = 0;
		if (userId.equals("kevin")) {
			result = 18;
		} else if (userId.equals("roy")) {
			result = 12;
		} else {
			result = 28;
		}
		
		return result;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

添加测试用服务类 TestService,使用 TestDAO 从数据库(模拟)中获取用户年龄。

package com.example.test.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.test.dao.TestDAO;

@Service
public class TestService {
	
	@Autowired
	TestDAO testDAO;
	
	public int getUserAge(String userId) {
		return testDAO.getUserAge(userId);
	}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在测试启动类中添加测试方法 testService,使用注入的 TestService 获取用户kevin的年龄,并做相等断言(年龄等于18)。

@Autowired
TestService testService;

@Test
void testService() {
    int age = testService.getUserAge("kevin");
    assertEquals(18, age);
}
1
2
3
4
5
6
7
8

运行测试,查看测试结果。

image-20191226235213375

# 11.1.3 使用 Mock 测试控制层

由于控制器层会从浏览器接收用户的输入,所以在对测试控制层进行单元测试时,需要对 Spring MVC 和 Servlet 容器进行模拟,具体的模拟类为 MockMvc。

修改测试启动类,为其添加@AutoConfigureMockMvc注解,启动 MockMvc,创建 testControllerGetUserAge 测试方法,注意理解其中使用的 MockMvc.perform 方法,MockMvcRequestBuilders.get 方法,MockMvcResultMatchers.status 方法和 MockMvcResultMatchers.content 方法。

package com.example.test;

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.MockMvcResultMatchers;

@SpringBootTest
@AutoConfigureMockMvc
class SpringBootTestApplicationTests {

	@Test
	void testControllerGetUserAge(@Autowired MockMvc mvc) throws Exception {
		mvc.perform(MockMvcRequestBuilders.get("/test/getUserAge?userId=kevin")
				.accept(MediaType.TEXT_PLAIN))
				.andExpect(MockMvcResultMatchers.status().isOk())
				.andExpect(MockMvcResultMatchers.content().string("User[kevin]'s age is: 18."));
	}
	
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

运行 JUnit Test,检查测试结果:

image-20191227004050579

# 11.1.4 运行服务器后测试

有时候我们需要在一个完整运行的服务器中进行控制层的测试(模拟用户的浏览器操作行为),这个时候,我们可以使用 Spring Boot 提供的 TestRestTemplate 模板类来完成对运行中的服务器进行测试。

这个行为,已经不太像单元测试了,我个人认为有点儿像自动化测试了。

package com.example.test;

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.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RandomPortTestRestTemplateExampleTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/test/getUserAge?userId=kevin", String.class);
        Assertions.assertThat(body).isEqualTo("User[kevin]'s age is: 18.");
    }
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

运行 JUnit Test,然后查看测试结果:

image-20191227010648549

本小节示例项目代码:

https://github.com/gyzhang/SpringBootCourseCode/tree/master/spring-boot-test (opens new window)

编辑 (opens new window)
上次更新: 2024/11/17, 16:29:23
单元测试与热部署
热部署

← 单元测试与热部署 热部署→

最近更新
01
PNG图片处理C++
02-07
02
PNG图片处理
01-24
03
离线安装Docker
12-24
更多文章>
Theme by Vdoing | Copyright © 2008-2025 Kevin Zhang | MIT License | 蜀ICP备20013663号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式