杰瑞科技汇

Python unittest模块如何使用?

Python 的 unittest 模块是 Python 标准库中内置的单元测试框架,它的灵感来源于 Java 的 JUnit,支持测试自动化、共享测试配置、聚合测试报告以及独立于报告框架的测试运行。

Python unittest模块如何使用?-图1
(图片来源网络,侵删)

以下是使用 unittest 模块的核心指南,从基础用法到进阶技巧。


核心概念

unittest 主要由以下几个部分组成:

  • Test Case (测试用例): 测试的个体单元,通过继承 unittest.TestCase 类来创建。
  • Test Suite (测试套件): 用于聚合多个测试用例,以便一起运行。
  • Test Runner (测试运行器): 负责执行测试并输出结果(如控制台输出或图形界面)。
  • Test Fixture (测试装置): 测试的准备和清理工作(如创建临时数据库、打开文件等)。

快速上手:编写第一个测试

假设你有一个简单的函数需要测试:

被测试代码 (my_math.py):

Python unittest模块如何使用?-图2
(图片来源网络,侵删)
def add(a, b):
    return a + b

测试代码 (test_my_math.py):

import unittest
from my_math import add
# 1. 创建测试类,必须继承 unittest.TestCase
class TestMathFunctions(unittest.TestCase):
    # 2. 测试方法必须以 'test_' 开头
    def test_add_integers(self):
        result = add(10, 5)
        # 3. 使用断言方法检查结果
        self.assertEqual(result, 15)
    def test_add_strings(self):
        result = add("hello ", "world")
        self.assertEqual(result, "hello world")
# 4. 允许脚本直接运行
if __name__ == '__main__':
    unittest.main()

常用断言方法

unittest.TestCase 提供了丰富的断言方法,如果断言失败,测试会标记为 FAIL;如果抛出异常,标记为 ERROR。

方法
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNone(x) x is None
assertIn(a, b) a in b
assertIsInstance(a, b) isinstance(a, b)
assertRaises(Error, Func, *Args) 调用 Func 时抛出了 Error 异常
assertAlmostEqual(a, b) round(a-b, 7) == 0 (用于浮点数比较)

测试装置

用于在测试执行前准备环境,执行后清理环境。

  • setUp(): 每个测试方法运行前执行。
  • tearDown(): 每个测试方法运行后执行。
  • setUpClass(): 整个测试类运行前执行一次(必须加 @classmethod 装饰器)。
  • tearDownClass(): 整个测试类运行后执行一次(必须加 @classmethod 装饰器)。

示例:

Python unittest模块如何使用?-图3
(图片来源网络,侵删)
import unittest
class TestDatabase(unittest.TestCase):
    def setUp(self):
        # 比如在这里建立数据库连接,或创建临时文件
        self.db_connection = "Connected to DB"
        print("\nsetUp: 准备资源")
    def tearDown(self):
        # 关闭连接,删除临时文件
        self.db_connection = None
        print("tearDown: 清理资源")
    def test_query(self):
        print("Running test_query")
        self.assertEqual(self.db_connection, "Connected to DB")
    def test_insert(self):
        print("Running test_insert")
        # 这里也可以使用 self.db_connection
        self.assertIsNotNone(self.db_connection)
if __name__ == '__main__':
    unittest.main()

跳过测试与预期失败

有时你希望某些测试在某些条件下不执行(例如特定操作系统或未完成的功能)。

import unittest
import sys
class TestSkipping(unittest.TestCase):
    @unittest.skip("演示直接跳过")
    def test_nothing(self):
        self.fail("我不应该运行")
    @unittest.skipIf(sys.version_info < (3, 8), "需要 Python 3.8 以上版本")
    def test_python_version(self):
        # 仅在 Python 3.8+ 运行
        self.assertTrue(True)
    @unittest.skipUnless(sys.platform.startswith("win"), "仅在 Windows 上运行")
    def test_windows_feature(self):
        self.assertTrue(True)
    @unittest.expectedFailure
    def test_fail(self):
        # 如果测试失败,不计入失败数,而是标记为 expected failure
        self.assertEqual(1, 0)
if __name__ == '__main__':
    unittest.main()

运行测试

方法 A:命令行运行(推荐)

这是最常用的方式,unittest 会自动发现测试文件。

# 运行当前目录下所有以 test*.py 命名的文件
python -m unittest discover
# 运行特定模块
python -m unittest test_my_math
# 运行特定类
python -m unittest test_my_math.TestMathFunctions
# 运行特定方法
python -m unittest test_my_math.TestMathFunctions.test_add_integers
# 显示更详细的输出
python -m unittest -v test_my_math

方法 B:直接运行脚本

在测试文件末尾添加:

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

然后直接运行 python test_my_math.py


进阶:参数化

标准库的 unittest 不直接支持参数化(即用不同参数运行同一个测试逻辑),通常需要使用第三方库 parameterized

安装:

pip install parameterized

使用:

import unittest
from parameterized import parameterized
class TestAdd(unittest.TestCase):
    @parameterized.expand([
        (1, 1, 2),
        (2, 3, 5),
        (10, 5, 15),
        (0, 0, 0),
    ])
    def test_add(self, a, b, expected):
        from my_math import add
        self.assertEqual(add(a, b), expected)

Mock 对象

在单元测试中,我们经常需要隔离外部依赖(如 API 请求、数据库、时间函数)。unittest.mock (Python 3.3+ 内置) 提供了强大的 Mock 功能。

from unittest.mock import Mock
# 创建一个模拟对象
m = Mock()
# 设置返回值
m.some_method.return_value = 100
# 调用
result = m.some_method("arg1", "arg2")
# 断言
assert result == 100
# 检查是否被调用过
m.some_method.assert_called()
# 检查是否以特定参数被调用
m.some_method.assert_called_with("arg1", "arg2")
  1. 文件命名:测试文件最好以 test_ 开头。
  2. 类命名:测试类继承 unittest.TestCase
  3. 方法命名:测试方法必须以 test_ 开头。
  4. 断言:使用 self.assertEqual 等方法,不要用原生的 assert
  5. 运行:使用 python -m unittest discover 进行批量测试。
分享:
扫描分享到社交APP
上一篇
下一篇