【单元测试】为什么需要单元测试

1.什么是单元测试

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

什么意思呢?

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

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

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

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

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

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

2.为什么需要单元测试

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

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

  • 改进代码:编写单元测试的过程中,开发者能够再次审视业务流程和功能的实现,更容易发现一些代码上的问题。比如将复杂的模块进一步拆解为可测试的单元。
  • 利于重构:如果已经编写了一套可自动执行的单元测试代码,那么每次修改代码或重构后,只需要再自动执行一遍单元测试,就知道修改是否正确了,能够大幅提高效率和项目稳定性。
  • 文档沉淀:编写详细的单元测试本身也可以作为一种文档,说明代码的预期行为。

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

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

3.如何编写单元测试

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

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

3.1 引入 JUnit

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

3.1.1 Maven 项目引入

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

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

3.1.2 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。

3.2 编写单元测试

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

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

3.2.1 编写 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(是否为空)等,用来对比程序实际输出的值和我们预期的值是否一致。

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

在这里插入图片描述

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

在这里插入图片描述

3.2.2 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 = "pipi";
        String userPassword = "";
        String checkPassword = "123456";
        // 执行测试
        long result = userService.userRegister(userAccount, userPassword, checkPassword);
        // 验证结果
        Assertions.assertEquals(-1, result);
        // 再准备一组数据,重复测试流程
        userAccount = "pi";
        result = userService.userRegister(userAccount, userPassword, checkPassword);
        Assertions.assertEquals(-1, result);
    }
}

3.3 生成测试报告

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

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

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

3.3.1 使用 IDEA 生成单测报告

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

在这里插入图片描述

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

图片

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

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

在这里插入图片描述

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

在这里插入图片描述

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

3.3.2 使用 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 文件,就能看到具体的测试报告结果,非常清晰:

图片

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

  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

G皮T

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

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

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

打赏作者

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

抵扣说明:

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

余额充值