10. 单元测试

目录

1、前言

2、单元测试

3、unittest

3.1、定义业务模块代码

3.2、定义单元测试代码

3.3、执行单元测试方式

[4、setUp & tearDown](#4、setUp & tearDown)

5、小结


1、前言

软件测试是整个软件开发生命周期内的一个重要阶段,通常软件测试可以评估和验证软件系统的质量、可靠性、安全性和性能等方面。测试通过执行软件的一系列操作,旨在发现潜在的错误、缺陷或问题,从而确保软件能够按照预期工作。而软件测试往往覆盖了不同的层次和类型,其中单元测试是针对软件中最小的独立单元(通常是函数或方法)进行的测试。目标是确保每个单元独立地工作,并且对输入产生正确的输出。单元测试通常由开发人员编写,用于验证代码的正确性。

2、单元测试

单元测试是软件开发中的一种测试方法,用于验证代码中的最小单元(通常是函数或方法)是否按照预期工作。单元测试旨在隔离和测试软件的各个独立部分,确保每个部分的行为都是正确的。

Python 中,单元测试是通过使用 unittest 模块来实现的。unittest 模块提供了一个测试框架,允许开发者编写和执行测试用例,以确保代码的正确性。

3、unittest

unittest是python自带的用于单元测试的模块,类似java中的junit。

3.1、定义业务模块代码

首先我们先定义一个普通使用的模块代码math_operator.py,提供了一系列方法:

python 复制代码
def add(x, y):
    return x + y


def sub(x, y):
    return x - y


def mul(x, y):
    return x * y


def div(x, y):
    if y == 0:
        raise ValueError("Cannot divide by zero")
    return x / y

3.2、定义单元测试代码

接着定义math_operator.py的单元测试代码math_operator_unittest.py:

python 复制代码
import math_operator
import unittest


class TestMathOperator(unittest.TestCase):

    def test_add(self):
        print("我只是单元测试add方法")
        self.assertEqual(math_operator.add(1, 2), 3)  # 理论上这个单元测试通过

    def test_sub(self):
        print("我只是单元测试sub方法")
        self.assertEqual(math_operator.sub(3, 2), 1)  # 理论上这个单元测试通过

    def test_mul(self):
        print("我只是单元测试mul方法")
        self.assertEqual(math_operator.mul(1, 2), 9)  # 理论上这个单元测试不通过

    def normal(self):
        print("我只是个普通方法")    # 他并不是单元测试函数,不会被执行

if __name__ == '__main__':
    unittest.main()

需要几个步骤:

  1. 引入python自带的单元测试模块unittest:import unittest,同时也把我们要单元测试的业务模块引入:import math_operator。
  2. 定义单元测试类TestMathOperator,需要继承unittest.TestCase。
  3. 针对每个方法定义单元测试代码,以test_xxx()。只有以test_开头的方法才其中才会被认为是单元测试函数,不然是普通函数,在执行单元测试套件时候是不会被执行的。
  4. 单元测试代码这里使用了断言assert,他会在判断成功后返回true,否则异常。
  5. 运行单元测试,unittest.main()。

看下运行结果:

可以看出,首先只有test_xxx()的方法被执行,一个普通的函数在里面是不会被执行的。其次,看终端提示的倒数第三行,显示运行了3个test case,并花费了0.004s。倒数第二行显示整个的单元测试套件结果是FAILED(不通过),不通过数量1个(failures=1)。再往上看,可以看出再执行mul方法时,期望结果是2(Expected: 2),但是实际结果却是9(Actual: 9)。实际结果与期望结果不符,因此测试不通过。

当我们手动修改一下test_mul()方法:

python 复制代码
def test_mul(self):
    print("我只是单元测试mul方法")
    self.assertEqual(math_operator.mul(1, 2), 2)

再此运行,发现测试用例通过。顶部提示Tests passed: 3。

3.3、执行单元测试方式

执行单元测试方式有多种,上面代码中,在某个单元测试类中直接加上入口函数,这种也是最简单的。

python 复制代码
if __name__ == '__main__':
    unittest.main()

还有一种方式是通过命令行执行:

bash 复制代码
python -m unittest math_operator_unittest.py

运行结果:

4、setUp & tearDown

前面我们已经能够编写一些简单的单元测试函数了,但是往往项目中除了对某个工具方法进行单元测试以外,还需要对一些服务进行测试,比如这些服务是需要连接数据库的,这时候就要求我们单元测试代码中具备连接数据库以及关闭数据库的能力。这时候就需要用到setUp和tearDown了。

在 Python 的 unittest 框架中,setUp 和 tearDown 方法是用于设置和清理测试环境的两个重要的特殊方法。这两个方法在每个测试方法执行前后分别被调用,以确保测试环境的准备和清理。

  1. setUp:在每个测试方法执行之前调用。通常用于准备测试环境,例如初始化变量、建立测试数据等,或在测试之前创建对象或设置必要的资源。
  2. tearDown :每个测试方法执行之后调用。通常用于清理测试环境,例如关闭文件、释放资源等,或在测试之后进行清理操作。

我们用上面的单元测试代码添加下setUp和tearDown方法:

python 复制代码
import math_operator
import unittest


class TestMathOperator(unittest.TestCase):

    def setUp(self) -> None:
        print("setUp()单元测试前执行,这里开启数据库连接")

    def tearDown(self) -> None:
        print("tearDown()单元测试后执行,这里关闭数据库连接")

    def test_add(self):
        print("我只是单元测试add方法")
        self.assertEqual(math_operator.add(1, 2), 3)

    def test_sub(self):
        print("我只是单元测试sub方法")
        self.assertEqual(math_operator.sub(3, 2), 1)

    def test_mul(self):
        print("我只是单元测试mul方法")
        self.assertEqual(math_operator.mul(1, 2), 2)

    def normal(self):
        print("我只是个普通方法")


if __name__ == '__main__':
    unittest.main()

运行一下,可以发现每个单元测试用例前后分别被setUp和tearDown包围,这个其实就是他们两的生命周期。

备注:

上面的def setUp(self) -> None: 是使用类型注解(Type Hinting)的 setUp 方法的声明。类型注解是 Python 3.5 及更高版本引入的特性,用于提供变量和函数参数的类型信息。在这个声明中,self 表示当前对象实例,而 -> None 表示该方法的返回类型是 None。这并不是强制性的,因为在 Python 中,函数和方法默认的返回类型是 None。而setUp 方法没有返回值(返回 None),因此使用 -> None 是一种良好的注释习惯,让读者知道这个方法的返回类型是 None。

5、小结

单元测试作为软件开发活动中的一项重要实践,有很多好处。不仅能提前发现一些潜在问题,确保代码正确性,减少回归问题。Bug往往越早期发现,修复成本越低,因而可以降低维护的成本。所以在日常开发活动中,还是要养成编写单元测试代码的习惯,通过建立一套健全的单元测试,开发者可以更自信地进行代码更改,并确保所做的修改不会对系统的整体功能产生负面影响。

相关推荐
想会飞的蒲公英4 小时前
计算机怎样读取中文文本:编码、清洗与标准化
人工智能·python·自然语言处理
SeaTunnel5 小时前
从 Python Script 地狱到标准化数据集成框架
大数据·开发语言·python·程序员·代码·seatunnel
深度研习笔记5 小时前
OpenCV工业视觉实战09|项目EXE打包+工控机无环境部署+后台常驻运行,彻底脱离Python环境,完成项目最终交付
python·opencv·webpack
CCPC不拿奖不改名5 小时前
大模型推理架构与开源生态知识整理
数据库·windows·python·架构·langchain·开源·github
Marst Code6 小时前
(python)2026Plotly 库评估:交互式可视化到底值不值得引入?
开发语言·python
databook6 小时前
当散点图不够用时:用 t-SNE 可视化多维数据
python·数据分析·数据可视化
我的xiaodoujiao8 小时前
快速学习Python基础知识详细图文教程9--函数进阶
开发语言·python·学习·测试工具
weixin_408099679 小时前
2026 图片去水印 API 接口完全指南:一键去除图片水印(附 Python/Java/PHP/C# 示例)
java·python·php·图片处理·api调用·图片去水印·石榴智能
去码头整点薯条ing9 小时前
某当网登录滑块【协议+OCR】
爬虫·python·ocr
川石课堂软件测试10 小时前
安全测试|常见SQL注入攻击方式、实例及预防
服务器·数据库·sql·功能测试·测试工具·安全·单元测试