为什么程序员一定要写单元测试?

大家好,我是鱼皮,很多初学编程的同学都会认为 “程序员的工作只有开发新功能,功能做完了就完事儿”。但其实不然,保证程序的正常运行、提高程序的稳定性和质量也是程序员的核心工作。

之前给大家分享过企业项目的完整开发流程,其中有一个关键步骤叫 “单元测试”,这篇文章就来聊聊程序员如何编写单元测试吧。

什么是单元测试?

单元测试(Unit Testing,简称 UT)是软件测试的一种,通常由开发者编写测试代码并运行。相比于其他的测试类型(比如系统测试、验收测试),它关注的是软件的 最小 可测试单元。

什么意思呢?

假如我们要实现用户注册功能,可能包含很多个子步骤,比如:

  1. 校验用户输入是否合法
  2. 校验用户是否已注册
  3. 向数据库中添加新用户

其中,每个子步骤可能都是一个小方法。如果我们要保证用户注册功能的正确可用,那么就不能只测试注册成功的情况,而是要尽量将每个子步骤都覆盖到,分别针对每个小方法做测试。比如输入各种不同的账号密码组合来验证 “校验用户输入是否合法” 这一步骤在成功和失败时的表现是否符合预期。

同理,如果我们要开发一个很复杂的系统,可能包含很多小功能,每个小功能都是一个单独的类,我们也需要针对每个类编写单元测试。因为只有保证每个小功能都是正确的,整个复杂的系统才能正确运行。

单元测试的几个核心要点是:

  1. 最小化测试范围:单元测试通常只测试代码的一个非常小的部分,以确保测试的简单和准确。
  2. 自动化:单元测试应该是自动化的,开发人员可以随时运行它们来验证代码的正确性,特别是在修改代码后。而不是每次都需要人工去检查。
  3. 快速执行:每个单元测试的执行时间不能过长,应该尽量做到轻量、有利于频繁执行。
  4. 独立性:每个单元测试应该独立于其他测试,不依赖于外部系统或状态,以确保测试的可靠性和可重复性。

为什么需要单元测试?

通过编写和运行单元测试,开发者能够快速验证代码的各个部分是否按照预期工作,有利于保证系统功能的正确可用,这是单元测试的核心作用。

此外,单元测试还有很多好处,比如:

1)改进代码:编写单元测试的过程中,开发者能够再次审视业务流程和功能的实现,更容易发现一些代码上的问题。比如将复杂的模块进一步拆解为可测试的单元。

2)利于重构:如果已经编写了一套可自动执行的单元测试代码,那么每次修改代码或重构后,只需要再自动执行一遍单元测试,就知道修改是否正确了,能够大幅提高效率和项目稳定性。

3)文档沉淀:编写详细的单元测试本身也可以作为一种文档,说明代码的预期行为。

鱼皮以自己的一个实际开发工作来举例单元测试的重要性。我曾经编写过一个 SQL 语法解析模块,需要将 10000 多条链式调用的语法转换成标准的 SQL 语句。但由于细节很多,每次改进算法后,我都不能保证转换 100% 正确,总会人工发现那么几个错误。所以我编写了一个单元测试来自动验证解析是否正确,每次改完代码后执行一次,就知道解析是否完全成功了。大幅提高效率。

所以无论是后端还是前端程序员,都建议把编写单元测试当做一种习惯,真的能够有效提升自己的编码质量。

如何编写单元测试?

以 Java 开发为例,我们来学习如何编写单元测试。

Java 开发中,最流行的单元测试框架当属 JUnit 了,它提供了一系列的类和方法,可以帮助我们快速检验代码的行为。

1、引入 JUnit

首先我们要在项目中引入 JUnit,演示 2 种方式:

Maven 项目引入

在 pom.xml 文件中引入 JUnit 4 的依赖:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>
Spring Boot 项目引入

如果在 Spring Boot 中使用 JUnit 单元测试,直接引入 spring-boot-starter-test 包即可:

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

然后会自动引入 JUnit Jupiter,它是 JUnit 5(新版本)的一部分,提供了全新的编写和执行单元测试的方式,更灵活易用。不过学习成本极低,会用 JUnit 4,基本就会用 JUnit Jupiter。

2、编写单元测试

编写一个单元测试通常包括三个步骤:准备测试数据、执行要测试的代码、验证结果。

一般来说,每个类对应一个单元测试类,每个方法对应一个单元测试方法。

编写 JUnit 单元测试

比如我们要测试一个计算器的求和功能,示例代码如下:

import org.junit.Test;
import org.junit.Assert;

public class CalculatorTest {

    // 通过 Test 注解标识测试方法
    @Test
    public void testAdd() {
        // 准备测试数据
        long a = 2;
        long b = 3;
        
        // 执行要测试的代码
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        
        // 验证结果
        Assert.assertEquals(5, result);
    }
}

上述代码中的 Assert 类是关键,提供了很多断言方法,比如 assertEquals(是否相等)、assertNull(是否为空)等,用来对比程序实际输出的值和我们预期的值是否一致。

如果结果正确,会看到如下输出:

如果结果错误,输出如下,能够清晰地看到执行结果的差异:

Spring Boot 项目单测

如果是 Spring Boot 项目,我们经常需要对 Mapper 和 Service Bean 进行测试,则需要使用 @SpringBootTest 注解来标识单元测试类,以开启对依赖注入的支持。

以测试用户注册功能为例,示例代码如下:

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

import javax.annotation.Resource;

@SpringBootTest
public class UserServiceTest {

    @Resource
    private UserService userService;

    @Test
    void userRegister() {
        // 准备数据
        String userAccount = "yupi";
        String userPassword = "";
        String checkPassword = "123456";
        // 执行测试
        long result = userService.userRegister(userAccount, userPassword, checkPassword);
        // 验证结果
        Assertions.assertEquals(-1, result);
        // 再准备一组数据,重复测试流程
        userAccount = "yu";
        result = userService.userRegister(userAccount, userPassword, checkPassword);
        Assertions.assertEquals(-1, result);
    }
}

3、生成测试报告

如果系统的单元测试数量非常多(比如 1000 个),那么只验证某个单元测试用例是否正确、查看单个结果是不够的,我们需要一份全面完整的单元测试报告,便于查看单元测试覆盖度、评估测试效果和定位问题。

测试覆盖度 是衡量测试过程中被测试到的代码量的一个指标,一般情况下越高越好。测试覆盖度 100% 表示整个系统中所有的方法和关键语句都被测试到了。

下面推荐 2 种生成单元测试报告的方法。

使用 IDEA 生成单测报告

直接在 IDEA 开发工具中选择 Run xxx with Coverage 执行单元测试类:

然后就能看到测试覆盖度报告了,如下图:

显然 Main 方法没有被测试到,所以显示 0%。

除了在开发工具中查看测试报告外,还可以导出报告为 HTML 文档:

导出后,会得到一个 HTML 静态文件目录,打开 index.html 就能在浏览器中查看更详细的单元测试报告了:

这种方式简单灵活,不用安装任何插件,比较推荐大家日常学习使用。

使用 jacoco 生成单测报告

JaCoCo 是一个常用的 Java 代码覆盖度工具,能够自动根据单元测试执行结果生成详细的单测报告。

它的用法也很简单,推荐按照官方文档中的步骤使用。

官方文档指路:https://www.eclemma.org/jacoco/trunk/doc/maven.html

首先在 Maven 的 pom.xml 文件中引入:

<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.11</version>
</plugin>

当然,只引入 JaCoCo 插件还是不够的,我们通常希望在执行单元测试后生成报告,所以还要增加 executions 执行配置,示例代码如下:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
    <configuration>
        <includes>
            <include>com/**/*</include>
        </includes>
    </configuration>
    <executions>
        <execution>
            <id>pre-test</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>post-test</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

然后执行 Maven 的 test 命令进行单元测试:

测试结束后,就能够在 target 目录中,看到生成的 JaCoCo 单元测试报告网站了:

打开网站的 index.html 文件,就能看到具体的测试报告结果,非常清晰:

通常这种方式会更适用于企业中配置流水线来自动化生成测试报告的场景。

实践

编程导航星球的用户中心项目详细讲解了如何使用 JUnit 编写规范的单元测试。

👉🏻 编程导航原创项目教程系列:https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分享一个在线免费制作和视频合成gif的网站

一、打开网站 在线制作高清gif动图-视频转gif表情工具-图片合成软件-gif.cn_GIF中文网 如图 二、可以选择gif合成&#xff0c;也就是把多张图片合成gif 效果图&#xff0c;我用了三张图片。 三、可以选择视频转gif。 效果图 四、完

RT-DETR算法优化改进:Backbone改进 | LSKNet:遥感旋转目标检测新SOTA | ICCV 2023

💡💡💡本文独家改进:LSKNet 助力RT-DETR ,替换backbone,Large Selective Kernel Network (LSKNet),可以动态地调整其大空间感受野,以更好地建模遥感场景中各种物体的测距的场景。 推荐指数:五星 RT-DETR魔术师专栏介绍: https://blog.csdn.net/m0_63774211/cat…

Git学习(黑马程序员)

基本配置 在用户目录下创建文件.bashrc 1打开Git Bash 2 在目录下输入指令&#xff1a; touch ~/.bashrc在文件中写入内容&#xff1a; #用于输出git提交日志 alias git-loggit log --prettyoneline --all --graph --abbrev-commit #用于输出当前目录所有文件及基本信息 al…

百度搜索智能化算力调控分配方法

作者 | 泰来 导读 随着近年深度学习技术的发展&#xff0c;搜索算法复杂度不断上升&#xff0c;算力供给需求出现了爆发式的增长。伴随着AI技术逐步走到深水区&#xff0c;算法红利在逐步消失&#xff0c;边际效应日益显著&#xff0c;算力效能的提升尤为重要&#xff0c;同时随…

【接口自动化测试】Postman(一) 介绍和安装

一.Postman介绍 Postman是一款非常流行的接口调试工具&#xff0c;它使用简单&#xff0c;而且功能也很强大。不仅测试人员会使用&#xff0c;开发人员也会 经常使用。 主要特点 1. 简单易用的图形用户界面 2. 可以保存接口请求的历史记录 3. 使用测试集Collections可以更…

rocketmq5.X 单机搭建 虚拟机搭建rocketmq5.1.4 搭建最新版本mq rocketmq5.1.4版本单体搭建 rocketmq(一)

1. 官网下载地址&#xff1a; 下载 | RocketMQ 2. 配置环境&#xff1a; 我是在/etc/profile.d 新建了一个rocketmq_env.sh 配置了jdk, maven, 以及mq. mq文件下载的 配置完之后&#xff0c;刷新环境source /etc/profile 3. 配置rocket mq 的jvm配置&#xff0c;就是两个启…

OpenAI:我们暂停了ChatGPT Plus新用户注册

今天中午&#xff0c;OpenAI 首席执行官 Sam Altman 在 X 平台发文说&#xff0c;将暂停 ChatGPT Plus 新用户注册。 we are pausing new ChatGPT Plus sign-ups for a bit > :( the surge in usage post devday has exceeded our c> apacity and we want to make sure e…

UI自动化测试框架的搭建(详解)

前言 今天给大家分享一个seleniumtestngmavenant的UI自动化&#xff0c;可以用于功能测试&#xff0c;也可按复杂的业务流程编写测试用例&#xff0c;今天此篇文章不过多讲解如何实现CI/CD&#xff0c;只讲解自己能独立搭建UI框架&#xff0c;需要阅读者有一定的java语言基础&…

Redis概述

Redis是一款NoSql(非关系型)数据库&#xff0c;实现了主从同步。 使用场景&#xff1a; 对数据高并发的读写。 海量数据的读写。 对数据的可扩展性的。 NoSql数据库举例&#xff1a; Memcache&#xff1a;数据都在内存中&#xff0c;但是数据不持久化&#xff0c;而且只支…

巷议:跌落尘埃与风光无限

近几来制造业的退潮是不争的事实&#xff0c;其中以老资格直辖市天津尤为突出。曾记否&#xff0c;想当年韩国的三星集团是天津最强的外企&#xff0c;但是从2015年开始便撤离了&#xff0c;给天津经济带来了重创。 而天津的汽车产业&#xff0c;也日渐变得软弱。其中那曾经小…

JavaScript语法、语句、数据类型

一、JavaScript语法&#xff1a; 1、JavaScript字面量&#xff1a; JavaScript中的固定值称为字面量。数字字面量可以是整数、小数或者科学计数&#xff08;e&#xff09;,如3.1415926、1008、123e5等&#xff1b;字符串字面量可以使用单引号或者双引号&#xff0c;如“corli…

用于部署汽车AI项目的全面自动化数据流程

如何创建、优化和扩展汽车 AI 的数据流程 想到汽车行业的人工智能 (AI) 时&#xff0c;脑海中可能会立即浮现未来的道路上遍布自动驾驶汽车的情景。虽然这一切尚未实现&#xff0c;但汽车行业已在 AI 方面取得诸多进步&#xff0c;不仅安全性提高&#xff0c;车内体验也得到改…

数据库sql语句设置外键

当我们需要在数据库表之间建立关联关系时&#xff0c;可以使用外键&#xff08;Foreign Key&#xff09;来实现。在 SQL 中&#xff0c;外键可以用来保持数据的完整性&#xff0c;并帮助我们更有效地管理数据。以下是设置外键的步骤&#xff1a; 1.在创建表时&#xff0c;需要…

系统之家重装Win10系统教程图解

系统之家官网给用户们提供了不同品牌系统的下载&#xff0c;帮助更多的用户完成Win10系统的重新安装&#xff0c;从而解决自己Win10系统所遇到的问题。如果有用户不清楚详细的重装系统步骤&#xff0c;那么可以参考下面小编分享借助系统之家装机大师软件重装Win10系统教程图解介…

企业微信H5开发遇到的坑

企业微信官方推荐wx.agentConfig引用<script src"https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>是没有效果的 必须引用以下代码才有效果&#xff0c;这也是我看了社区的回答才有所收获&#xff0c;是一个坑 且VUE引用在线的…

Page分页records有数据,但是total=0,解决办法

Page分页records有数据&#xff0c;但是total0&#xff0c;解决办法 问题&#xff1a;程序运行起来后&#xff0c;后端接收前端传来的搜索请求信息正常&#xff0c;但无法在前端正确反馈信息&#xff0c;通过在后端排查发现total一直等于零&#xff0c;但数据库中有数据&#x…

需求工程咨询和实施服务

服务概述 多年来经纬恒润在汽车电子产品开发与量产、工程服务、研发流程体系建设方面积累了大量的实际研发经验&#xff0c;并为国内外主流OEM和核心供应商提供了相应的量产产品和研发服务&#xff0c;覆盖车身和舒适域、智能驾驶、智能网联、智能座舱、底盘控制、新能源及动力…

正则提取记录

使用正则 https?:\/\/([^\/\s])/

RocketMQ中的消息种类以及消费模式

RocketMQ中的消息种类以及消费模式 前言消息的种类按消息的发送方式同步消息异步消息单向消息 按消息的种类普通消息&#xff08;Normal Message&#xff09;顺序消息&#xff08;Orderly Message&#xff09;延时消息&#xff08;Delay Message&#xff09;事务消息&#xff0…

【深度学习 | 核心概念】那些深度学习路上必经的核心概念,确定不来看看? (六)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…