Skip to content

Python基础 - unittest 使用简明教程

约 950 字大约 3 分钟

Python

2025-10-24

概述

unittest 是 Python 标准库中的单元测试框架,深受 Java 的 JUnit 框架影响。它支持测试自动化、共享设置和关闭代码、测试聚合以及测试独立于报告框架。本教程将介绍如何使用 unittest 编写和运行测试。

基本概念

在深入学习之前,先了解几个核心概念:

  • TestCase: 测试用例的基本类,所有测试类都继承此类

  • TestSuite: 测试套件,用于组合多个测试用例

  • TestRunner: 测试运行器,负责执行测试并输出结果

  • TestFixture: 测试夹具,通过 setUp 和 tearDown 方法管理测试环境

编写测试用例

创建测试类需继承 unittest.TestCase,测试方法必须以 test_ 开头:

import unittest

# 被测函数
def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ValueError("除数不能为零")
    return a / b

# 测试类
class TestMathFunctions(unittest.TestCase):
    
    def test_add(self):
        """测试加法函数"""
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(-1, 1), 0)
    
    def test_divide(self):
        """测试除法函数"""
        self.assertEqual(divide(6, 2), 3)
    
    def test_divide_by_zero(self):
        """测试除零异常"""
        with self.assertRaises(ValueError):
            divide(10, 0)

常用断言方法

unittest 提供了丰富的断言方法用于验证测试结果:

class TestAssertions(unittest.TestCase):
    
    def test_assertions(self):
        # 相等断言
        self.assertEqual(1, 1)
        self.assertNotEqual(1, 2)
        
        # 真假断言
        self.assertTrue(1 == 1)
        self.assertFalse(1 == 2)
        
        # None 断言
        self.assertIsNone(None)
        self.assertIsNotNone(1)
        
        # 包含断言
        self.assertIn(1, [1, 2, 3])
        self.assertNotIn(4, [1, 2, 3])
        
        # 类型断言
        self.assertIsInstance(1, int)
        self.assertNotIsInstance("hello", int)
        
        # 近似相等
        self.assertAlmostEqual(0.1 + 0.2, 0.3, places=7)

测试夹具 (Test Fixture)

测试夹具用于设置测试环境和清理资源:

class TestWithFixture(unittest.TestCase):
    
    @classmethod
    def setUpClass(cls):
        """整个类开始前执行一次"""
        print("=== 测试类开始 ===")
    
    @classmethod
    def tearDownClass(cls):
        """整个类结束后执行一次"""
        print("=== 测试类结束 ===")
    
    def setUp(self):
        """每个测试方法前执行"""
        print("--- 测试方法开始 ---")
    
    def tearDown(self):
        """每个测试方法后执行"""
        print("--- 测试方法结束 ---")
    
    def test_example(self):
        self.assertTrue(True)

跳过测试

支持有条件或无条件跳过某些测试:

class TestSkipExample(unittest.TestCase):
    
    @unittest.skip("直接跳过")
    def test_skip(self):
        self.fail("不会执行")
    
    @unittest.skipIf(1 > 0, "条件为真时跳过")
    def test_skip_if(self):
        self.fail("条件为真时不会执行")
    
    @unittest.skipUnless(1 < 0, "条件为假时跳过")
    def test_skip_unless(self):
        self.fail("条件为假时不会执行")
    
    @unittest.expectedFailure
    def test_expected_failure(self):
        self.assertEqual(1, 2)  # 预期会失败

参数化测试

通过第三方库 parameterized 实现参数化测试:

pip install parameterized
from parameterized import parameterized

class TestParameterized(unittest.TestCase):
    
    @parameterized.expand([
        (1, 1, 2),
        (2, 3, 5),
        (-1, -1, -2),
        (0, 0, 0)
    ])
    def test_add_parameterized(self, a, b, expected):
        self.assertEqual(add(a, b), expected)

测试套件

测试套件允许将多个测试用例组合在一起运行:

def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestMathFunctions('test_add'))
    suite.addTest(TestMathFunctions('test_divide'))
    return suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

运行测试

提供多种运行测试的方式:

方法1: 直接运行

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

方法2: 命令行运行

# 运行所有测试
python -m unittest

# 运行指定模块
python -m unittest test_module

# 运行指定类
python -m unittest test_module.TestClass

# 运行指定方法
python -m unittest test_module.TestClass.test_method

# 发现并运行所有测试
python -m unittest discover

# 发现指定目录的测试
python -m unittest discover -s project/tests

# 详细输出
python -m unittest -v

# 遇到第一个失败就停止
python -m unittest -f

## 测试组织建议
合理的测试目录结构有助于维护测试代码:

```text
project/
├── src/
   └── mymodule.py
└── tests/
    ├── __init__.py
    ├── test_module1.py
    ├── test_module2.py
    └── integration/
        └── test_integration.py

总结

unittest 提供了完整的测试解决方案,从简单的单元测试到复杂的集成测试。通过合理组织测试代码和使用各种测试特性,可以构建健壮、可维护的测试套件,确保代码质量。

更多参考