【单元测试】一文读懂java单元测试

目录

  • 1. 什么是单元测试
  • 2. 为什么要单元测试
  • 3. 单元测试框架 - JUnit
    • 3.1 JUnit 简介
    • 3.2 JUnit 内容
    • 3.3 JUnit 使用
      • 3.3.1 Controller 层单元测试
      • 3.3.2 Service 层单元测试
      • 3.3.3 Dao 层单元测试
      • 3.3.4 异常测试
      • 3.3.5 测试套件测多个类
      • 3.3.6 idea 中查看单元测试覆盖率
      • 3.3.7 JUnit 插件自动生成单测代码
      • 3.3.8 常用注解和配置

1. 什么是单元测试

单元测试是软件开发中常用的一种测试方法,用于验证代码的单个功能单元是否按照预期工作。
测试方法:

  • 白盒测试(White Box Testing):在白盒测试中,测试人员了解代码的内部结构和实现细节,编写测试用例来覆盖不同的代码路径和逻辑条件。
  • 黑盒测试(Black Box Testing):黑盒测试不考虑代码的内部实现,而是基于需求规格说明或功能规范编写测试用例,测试程序的输入和输出是否符合预期。
  • 单元测试框架:使用单元测试框架可以简化单元测试的编写和执行。常见的单元测试框架包括JUnit(Java)、NUnit(.NET)、pytest(Python)等。
  • 断言(Assertion):在单元测试中,断言用于检查预期结果和实际结果是否匹配。测试人员可以使用断言来验证程序的特定行为和结果。
  • 边界值测试(Boundary Value Testing):边界值测试通过选择测试用例中的边界条件,例如最小值、最大值、临界值等,来验证程序在边界情况下的行为。
  • 异常处理测试(Exception Handling Testing):异常处理测试用于验证程序在遇到异常情况时是否能够正确地捕获和处理异常,并保证系统的稳定性和可靠性。
  • 参数化测试(Parameterized Testing):参数化测试允许在单个测试用例中使用不同的参数进行多次测试,以增加测试覆盖率和复用性。
    这些方法都可以根据具体的需求和开发环境选择使用。单元测试的目标是尽可能地覆盖代码,确保每个单元都能按照预期工作,并提高软件的质量和可维护性。

这些方法都可以根据具体的需求和开发环境选择使用。单元测试的目标是尽可能地覆盖代码,确保每个单元都能按照预期工作,并提高软件的质量和可维护性。

2. 为什么要单元测试

(1)单元测试意义:

程序代码都是由基本单元不断组合成复杂的系统,底层基本单元都无法保证输入输出正确性,层级递增时,问题就会不断放大,直到整个系统崩溃无法使用。所以单元测试的意义就在于保证基本功能是正常可用且稳定的。而对于接口、数据源等原因造成的不稳定因素,是外在原因,不在单元测试考虑范围之内。

(2)使用 main 方法进行测试:

@PostMapping("/add")
public void addStudent(@RequestBody Student student){studentService.save(student);
}

假如要对上面的 Controller 进行测试,可以编写如下的代码示例,使用 main 方法进行测试的时候,先启动整个工程应用,然后编写 main 方法如下进行访问,在单步调试代码。

public static void main(String[] args) {RestTemplate restTemplate = new RestTemplate();HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.setContentType(MediaType.APPLICATION_JSON);String json = "{\"id\":4,\"name\":\"阿狸\",\"classname\":\"初三一班\",\"age\":16,\"sex\":\"女\"}";HttpEntity<String> httpEntity = new HttpEntity<>(json, httpHeaders);String url = "http://localhost:8080/student/add";ResponseEntity<Map> responseEntity = restTemplate.postForEntity(url, httpEntity, Map.class);System.out.println(responseEntity.getBody());
}

(3)使用 main 方法进行测试的缺点:

  1. 通过编写大量的 main 方法针对每个内容做打印输出到控制台枯燥繁琐,不具备优雅性。

  2. 测试方法不能一起运行,结果需要程序员自己判断正确性。

  3. 统一且重复性工作应该交给工具去完成。

3. 单元测试框架 - JUnit

3.1 JUnit 简介

Unit 官网:https://junit.org/。JUnit 是一个用于编写可重复测试的简单框架。它是用于单元测试框架的 xUnit 体系结构的一个实例。

JUnit 的特点:

(1) 针对于 Java 语言特定设计的单元测试框架,使用非常广泛。

(2) 特定领域的标准测试框架。

(3) 能够在多种 IDE 开发平台使用,包含 Idea、Eclipse 中进行集成。

(4) 能够方便由 Maven 引入使用。

(5) 可以方便的编写单元测试代码,查看测试结果等。

JUnit 的重要概念:

名称功能作用
Assert断言方法集合
TestCase表示一个测试案例
TestSuite包含一组 TestCase,构成一组测试
TestResult收集测试结果

JUnit 的一些注意事项及规范:

(1) 测试方法必须使用 @Test 修饰

(2) 测试方法必须使用 public void 进行修饰,不能带参数

(3) 测试代码的包应该和被测试代码包结构保持一致

(4) 测试单元中的每个方法必须可以独立测试,方法间不能有任何依赖

(5) 测试类一般使用 Test 作为类名的后缀

(6) 测试方法使一般用 test 作为方法名的前缀

JUnit 失败结果说明:

(1) Failure:测试结果和预期结果不一致导致,表示测试不通过

(2) error:由异常代码引起,它可以产生于测试代码本身的错误,也可以是被测代码的 Bug

3.2 JUnit 内容

(1) 断言的 API

断言方法断言描述
assertNull(String message, Object object)检查对象是否为空,不为空报错
assertNotNull(String message, Object object)检查对象是否不为空,为空报错
assertEquals(String message, Object expected, Object actual)检查对象值是否相等,不相等报错
assertTrue(String message, boolean condition)检查条件是否为真,不为真报错
assertFalse(String message, boolean condition)检查条件是否为假,为真报错
assertSame(String message, Object expected, Object actual)检查对象引用是否相等,不相等报错
assertNotSame(String message, Object unexpected, Object actual)检查对象引用是否不等,相等报错
assertArrayEquals(String message, Object[] expecteds, Object[] actuals)检查数组值是否相等,遍历比较,不相等报错
assertArrayEquals(String message, Object[] expecteds, Object[] actuals)检查数组值是否相等,遍历比较,不相等报错
assertThat(String reason, T actual, Matcher<? super T> matcher)检查对象是否满足给定规则,不满足报错

(2) JUnit 常用注解:

1) @Test: 定义一个测试方法 @Test (excepted=xx.class): xx.class 表示异常类,表示测试的方法抛出此异常时,认为是正常的测试通过的 @Test (timeout = 毫秒数) : 测试方法执行时间是否符合预期。

2) @BeforeClass在所有的方法执行前被执行,static 方法全局只会执行一次,而且第一个运行

3) @AfterClass在所有的方法执行之后进行执行,static 方法全局只会执行一次,最后一个运行

4) @Before在每一个测试方法被运行前执行一次

5) @After在每一个测试方法运行后被执行一次

6) @Ignore:所修饰的测试方法会被测试运行器忽略。

7) @RunWith:可以更改测试执行器使用 junit 测试执行器。

3.3 JUnit 使用

3.3.1 Controller 层单元测试

(1) Springboot 中使用 maven 引入 Junit 非常简单,使用如下依赖即可引入:

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

(2) 上面使用 main 方法案例可以使用如下的 Junit 代码完成:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = UnittestDemoApplication.class)
class StudentControllerTest {@Autowiredprivate WebApplicationContext webApplicationContext;private MockMvc mockMvc;@BeforeEachvoid setUp() {mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();}@Testvoid testAddStudent() throws Exception {// 创建一个Student对象作为请求的JSON体Student student = new Student();student.setId(6);student.setName("小乔");student.setClassname("初二三班");student.setAge(14);student.setSex("女");// 将Student对象转换为JSON字符串ObjectMapper objectMapper = new ObjectMapper();String json = objectMapper.writeValueAsString(student);// 发送POST请求mockMvc.perform(MockMvcRequestBuilders.post("/student/add").contentType(MediaType.APPLICATION_JSON_UTF8).accept(MediaType.APPLICATION_JSON_UTF8).content(json))// 断言返回的状态码为200.andExpect(MockMvcResultMatchers.status().isOk())// 断言返回的JSON中包含指定的code和message.andExpect(MockMvcResultMatchers.jsonPath("$.code").value(200)).andExpect(MockMvcResultMatchers.jsonPath("$.message").value("保存成功")).andDo(MockMvcResultHandlers.print());}
}

只需要在类或者指定方法上右键执行即可,可以直接充当 postman 工作访问指定 url,且不需要写请求代码,这些都由工具自动完成。

在这里插入图片描述

(3)案例中相关组件介绍

本案例中构造 mockMVC 对象时,也可以使用如下方式:

@Autowired
private StudentController studentController;
@Before
public void setupMockMvc(){// 初始化MockMvc对象mockMvc = MockMvcBuilders.standaloneSetup(studentController).build();
}

其中 MockMVC 是 Spring 测试框架提供的用于 REST 请求的工具,是对 Http 请求的模拟,无需启动整个模块就可以对 Controller 层进行调用,速度快且不依赖网络环境。

使用 MockMVC 的基本步骤如下:

  1. mockMvc.perform 执行请求
  2. MockMvcRequestBuilders.post 或 get 构造请求
  3. MockHttpServletRequestBuilder.param 或 content 添加请求参数
  4. MockMvcRequestBuilders.contentType 添加请求类型
  5. MockMvcRequestBuilders.accept 添加响应类型
  6. ResultActions.andExpect 添加结果断言
  7. ResultActions.andDo 添加返回结果后置处理
  8. ResultActions.andReturn 执行完成后返回相应结果

3.3.2 Service 层单元测试

可以编写如下代码对 Service 层查询方法进行单测:

正例:

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentServiceTest {@Autowiredprivate StudentService studentService;@Testpublic void getOne(){Student stu = studentService.getById(1);Assert.assertThat(stu.getName(), CoreMatchers.is("张三"));}
}

执行结果:

在这里插入图片描述

反例:

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentServiceTest {@Autowiredprivate StudentService studentService;@Testpublic void getOne(){Student stu = studentService.getById(1);Assert.assertThat(stu.getName(), CoreMatchers.is("李四"));}
}

执行结果:

在这里插入图片描述

3.3.3 Dao 层单元测试

可以编写如下代码对 Dao 层保存方法进行单测:

@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentDaoTest {@Autowiredprivate StudentMapper studentMapper;@Test@Rollback(value = true)@Transactionalpublic void insertOne() throws Exception {Student student = new Student();student.setId(7);student.setName("王五");student.setClassname("大一");student.setAge(20);student.setSex("男");int count = studentMapper.insert(student);Assert.assertEquals(1, count);}
}

在这里插入图片描述
其中 @Rollback (value = true) 可以执行单元测试之后回滚所新增的数据,保持数据库不产生脏数据。

3.3.4 异常测试

(1) 在 service 层定义一个异常情况:

public void computeScore() {int a = 10, b = 0;int c = a/b;
}

(2) 在 service 的测试类中定义单元测试方法:

反例

    @Testpublic void computeScoreTest() {studentService.computeScore();}

结果:

在这里插入图片描述

正例:

junit 5.0版本之前,在@Test上添加expected = ArithmeticException.class

@Test(expected = ArithmeticException.class)public void computeScoreTest() {studentService.computeScore();}

junit 5.0版本之后,使用Assert.assertThrows

@Test
public void computeScoreTest() {Assert.assertThrows(ArithmeticException.class, () -> {studentService.computeScore(); // This line should throw ArithmeticException});
}

(3) 执行单元测试也会通过,原因是 @Test 注解中的定义了异常

在这里插入图片描述

3.3.5 测试套件测多个类

(1) 新建一个空的单元测试类

(2) 利用注解 @RunWith (Suite.class) @SuiteClasses 标明要一起单元测试的类

在这里插入图片描述

运行结果:

在这里插入图片描述

3.3.6 idea 中查看单元测试覆盖率

(1) 单测覆盖率

测试覆盖率是衡量测试过程工作本身的有效性,提升测试效率和减少程序 bug,提升产品可靠性与稳定性的指标。

统计单元测试覆盖率的意义:

1) 可以洞察整个代码中的基础组件功能的所有盲点,发现相关问题。

2) 提高代码质量,通常覆盖率低表示代码质量也不会太高,因为单测不通过本来就映射出考虑到各种情况不够充分。

3) 从覆盖率的达标上可以提高代码的设计能力。

(2) 在 idea 中查看单元测试覆盖率很简单,只需按照图中示例的图标运行,或者在单元测试方法或类上右键 Run ‘xxx’ with Coverage 即可。执行结果是一个表格,列出了类、方法、行数、分支覆盖情况。

在这里插入图片描述

(3) 在代码中会标识出覆盖情况,绿色的是已覆盖的,红色的是未覆盖的。

在这里插入图片描述
(4) 如果想要导出单元测试的覆盖率结果,可以使用如下图所示的方式,勾选 Open generated HTML in browser

在这里插入图片描述
导出结果:

在这里插入图片描述

3.3.7 JUnit 插件自动生成单测代码

(1) 安装插件

在这里插入图片描述
(2)选择要生成单元测试的类,按Alt+Insert出现如下界面,选择TestMe自动生成文件

在这里插入图片描述
(3)选择需要的生成模板,可以根据自己实际引入的依赖选择,此处选择Junit4+Mockito
在这里插入图片描述
(4)生成的代码如下,可以生成一些基本的方法和注解,然后根据实际情况修改,可以节省一部分工作量。
在这里插入图片描述

3.3.8 常用注解和配置

@Mock:创建一个模拟的对象,类似于@Autowired,但不是真实的对象,是Mock对象,这个注解使用在类属性上

@InjectMocks:创建一个实例,其余用@Mock注解创建的mock将被注入到用该实例中,这个注解使用在类属性上

@RunWith:表示一个运行器,@RunWith(PowerMockRunner.class)表示指定用PowerMockRunner运行,这个注解使用在类上

@PowerMockIgnore:这个注解表示将某些类延迟到系统类加载器加载,解决一些类加载异常。(具体类加载异常实际中还未遇见,后续补充),这个注解在类和方法上使用

@PrepareForTest:这个注解告诉PowerMock为测试准备某些类,通常是那些需要字节码操作的类。这包括带有final、private、static或native方法的类,new一个对象时,需要特殊处理(见下面的whenNew),这个注解在类和方法上使用

@Test:@Test修饰的public void方法可以作为测试用例运行。Junit会构造一个新的类实例,然后调用所有加了@Test的方法,方法执行过程中的任何异常,都会被判定为测试用例执行失败。

@Before:@Before注解的方法中的代码会在@Test注解的方法中首先被执行。可以做一些公共的前置操作:加入一些申请资源的代码:申请数据库资源,申请io资源,申请网络资源,new一些公共的对象等等。

@After:@After注解的方法中的代码会在@Test注解的方法中首先被执行。可以做一些公共的后置操作,如关闭资源的操作。

注:可以查看注解上的注释,了解其大致用法。

代码地址GitHub

单元测试之Mockito见文章《【单元测试】单元测试之Mockito的使用》

觉得有用的话还请来个三连!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/765349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第28章 ansible的使用

第28章 ansible的使用 本章主要介绍在 RHEL8 中如何安装 ansible 及 ansible的基本使用。 ◆ ansible 是如何工作的 ◆ 在RHEL8 中安装ansible ◆ 编写 ansible.cfg 和清单文件 ◆ ansible 的基本用法 文章目录 第28章 ansible的使用28.1 安装ansible28.2 编写ansible.cfg和清…

HDFS集群环境配置

环境如下三台服务器&#xff1a; 192.168.32.101 node1192.168.32.102 node2192.168.32.103 node3 一、Hadoop安装包下载&#xff0c;点此官网下载 二、Hadoop HDFS的角色包含&#xff1a; NameNode&#xff0c;主节点管理者DataNode&#xff0c;从节点工作者SecondaryNameN…

React Native: could not connect to development server

问题&#xff1a; 运行模拟器错误&#xff1a;无法连接到开发服务器 原因分析&#xff1a; 1、确认模拟器连接状态&#xff0c;是连接成功的 查看进程的端口占用&#xff0c;也没问题 lsof -i tcp:8081 kill pid2、检查包服务器是否运行正常 连接真机进行调试发现真机是正常…

基于springboot+vue+Mysql的“智慧食堂”设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Basic RNN

文章目录 回顾RNNRNN CellRNNCell的使用RNN的使用 RNN例子使用RNN Cell实现使用RNN实现 嵌入层 Embedding独热向量的缺点Embedding LSTMGRU(门控循环单元)练习 回顾 DNN&#xff08;全连接&#xff09;&#xff1a;和CNN相比&#xff0c;拥有巨大的参数量&#xff0c;CNN权重共…

游泳耳机哪个牌子好?强烈推荐这4大高性能款式!

在如今的科技时代&#xff0c;游泳耳机已经成为了许多游泳爱好者和运动员的必备装备。一款好的游泳耳机不仅可以让你在水中享受到美妙的音乐&#xff0c;还可以为你提供更好的训练体验。 &#xff08;下图是我测试过的一部分游泳耳机&#xff1a;&#xff09; 但在市场上众多的…

在线文本列表差集计算器

具体请前往&#xff1a;在线文本差集计算工具

云计算 3月22号 (mysql的主从复制)

一、MySQL-Replication&#xff08;主从复制&#xff09; 1.1、MySQL Replication 主从复制&#xff08;也称 AB 复制&#xff09;允许将来自一个MySQL数据库服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从服务器&#xff09;…

优化选址问题 | 基于禁忌搜索算法求解基站选址问题含Matlab源码

目录 问题代码问题 禁忌搜索算法(Tabu Search)是一种局部搜索算法的扩展,它通过引入一个禁忌列表来避免陷入局部最优解,并允许在一定程度上接受较差的解来跳出局部最优。在基站选址问题中,我们可以使用禁忌搜索算法来寻找满足覆盖要求且基站数量最少的选址方案。 以下是…

比赛记录:Codeforces Round 936 (Div. 2) A~E

传送门:CF [前题提要]:赛时一小时过了A~D,E感觉也不是很难(甚至感觉思维难度是小于D的),感觉这回是自己不够自信了,要是自信一点深入想一下应该也能做出来,咱就是说,如果E和D换一下,结果也是一样的,虽上大分,但是心里很不服,故记录一下 A - Median of an Array 当时网卡加载了…

手机网页视频批量提取工具可导出视频分享链接|爬虫采集下载软件

解放你的抖音视频管理——全新抖音批量下载工具震撼上线&#xff01; 在这个信息爆炸的时代&#xff0c;如何高效地获取、管理和分享视频内容成为了许多用户的迫切需求。为了解决这一难题&#xff0c;我们研发了全新的视频批量下载工具&#xff0c;让你轻松畅享海量音视频资源。…

SQL中条件放在on后与where后的区别

数据库在通过连接两张或多张表来返回记录时&#xff0c;都会生成一张中间的临时表&#xff0c;然后再将这张临时表返回给用户。 在使用left jion时&#xff0c;on和where条件的区别如下&#xff1a; on条件是在生成临时表时使用的条件&#xff0c;不管on中的条件是否为真&…

2024年 前端JavaScript Web APIs 第四天 笔记

4.1-日期对象的使用 4.2-时间戳的使用 4.3-倒计时案例的制作 4.4-查找DOM节点 4.5-增加节点以及学成在线案例 4.6-克隆节点和删除节点 4.7-M端事件 4.8-swiper插件的使用 4.9-今日综合案例-学生信息表 B站 <!DOCTYPE html> <html lang"en"><head>&…

ubuntu下samba匿名读写服务器

目的&#xff1a; 局域网内&#xff0c;ubuntu下&#xff0c;创建SAMBA文件共享服务器。匿名读写权限。为了开发项目组文件共享传输方便。 环境&#xff1a; X86_64电脑一台。 操作系统&#xff1a; Ubuntu 20.04 LTS 64位。 安装samba $ sudo apt-get install samba创建…

浅谈智能微型断路器在学校改造项目中的应用-安科瑞 蒋静

南浜路初中是昆山市重点建设的教育民生项目。当地政府对于这所学校非常重视&#xff0c;当然也有着很高的期望。南浜路初中的到来能够进一步促进昆山市义务教育阶段的发展&#xff0c;提升义务教育水平。 现场图片 智能网关 可连接至多16台智能微型断路器&#xff1b;可查看各…

UNI-APP读取本地JSON数据

首先要把json文件放在static文件夹下 然后在要读取数据的页面导入 import data from ../../static/data.json读取数据&#xff1a; onLoad() {console.log(data, data)}, 打印出来的就是JSON文件里的数据了

时序预测 | Matlab基于BiTCN-LSTM双向时间卷积长短期记忆神经网络时间序列预测

时序预测 | Matlab基于BiTCN-LSTM双向时间卷积长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab基于BiTCN-LSTM双向时间卷积长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab基于BiTCN-LSTM双向时间卷积长短期记忆神经网络时…

为什么光模块会发生故障?

当SFP光模块发生故障时&#xff0c;技术人员需要立即找出原因并进行修复&#xff0c;否则&#xff0c;1G链路可能会中断。本指南将为初次接触光模块领域的企业提供一些SFP光模块修复解决方案的支持。 SFP光模块故障的主要原因 SFP光模块故障通常发生在发送端和接收端。最常见…

C++夯实基础

C在线学习笔记 第一阶段&#xff1a;基础 一、环境配置 1.1.第一个程序&#xff08;基本格式&#xff09; ​ #include <iosteam> using namespace std;int main(){cout<<"hello world"<<endl;system("pause"); }​ 模板 #include…

eth 交易案例分析9

交易hash: 先用0.26eth买入了多个GPT&#xff0c;然后用这多个GPT 在uniswap3 兑换了1.69 个eth&#xff0c; 疑问点&#xff1a;买入的 DLP 什么意思&#xff1f;