# 单元测试与 pytest

URL: https://caijiao.org/python/ch11/testing
Source: docs/python/ch11/testing.md
Description: 本章介绍 Python 的单元测试框架 'unittest' 和第三方测试框架 'pytest'，以及如何编写和运行测试用例，确保代码的正确性。

单元测试是确保代码质量和正确性的重要手段。在 Python 中，我们可以使用 `unittest` 模块来进行单元测试，也可以使用更加灵活和易用的第三方框架 `pytest`。本章将介绍如何使用这两者编写测试用例并执行测试。

## 单元测试概述

单元测试是对代码中最小单元（通常是函数或方法）进行的验证，确保其在各种情况下按预期运行。[Python](/python/) 提供了 `unittest` 模块，能够支持编写和执行单元测试。

### 创建第一个单元测试

```python
import unittest

# 被测试的函数
def add(a, b):
    return a + b

# 测试类继承自 unittest.TestCase
class TestMathOperations(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(1, 2), 3)  # 验证 add(1, 2) 的返回值是否为 3

    def test_add_negative(self):
        self.assertEqual(add(-1, 1), 0)  # 验证 add(-1, 1) 的返回值是否为 0

if __name__ == "__main__":
    unittest.main()
```

### 运行测试

可以通过命令行运行 `unittest` 测试，使用以下命令：

```
python -m unittest test_module_name.py
```

或者，如果在脚本中加入了 `unittest.main()`，可以直接运行：

```
python test_module_name.py
```

### 断言方法

`unittest` 提供了多种断言方法用于测试结果，比如：

- `assertEqual(a, b)`：检查 a 是否等于 b。
- `assertNotEqual(a, b)`：检查 a 是否不等于 b。
- `assertTrue(x)`：检查 x 是否为 True。
- `assertFalse(x)`：检查 x 是否为 False。
- `assertRaises(exception, func, *args, **kwargs)`：检查是否抛出了指定的异常。

## 使用 pytest

`pytest` 是一个非常流行的第三方 Python 测试框架，它比 `unittest` 更加简洁和易用。`pytest` 可以自动发现并执行测试用例，并且支持丰富的功能，比如参数化测试、测试夹具等。

### 安装 pytest

首先需要安装 `pytest`：

```
pip install pytest
```

### 使用 pytest 编写测试

`pytest` 使得编写测试变得更简单，不需要继承 `unittest.TestCase` 类。只需创建以 `test_` 开头的函数，并通过断言进行验证。

```python
# 被测试的函数
def add(a, b):
    return a + b

# 使用 pytest 编写测试
def test_add():
    assert add(1, 2) == 3

def test_add_negative():
    assert add(-1, 1) == 0
```

### 运行 pytest 测试

使用 `pytest` 运行测试非常简单，只需执行以下命令：

```
pytest test_module_name.py
```

`pytest` 会自动查找以 `test_` 开头的函数，并执行它们。

### 高级功能

#### 参数化测试

`pytest` 允许我们为同一个测试函数提供多组不同的参数，简化了重复测试的编写。

```python
import pytest

# 被测试的函数
def add(a, b):
    return a + b

# 参数化测试
@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (-1, 1, 0),
    (0, 0, 0),
])
def test_add(a, b, expected):
    assert add(a, b) == expected
```

### 测试夹具（Fixtures）

`pytest` 提供了夹具（fixtures）来简化测试准备工作，例如创建数据库连接、初始化测试数据等。夹具可以在测试函数中自动注入。

```python
import pytest

# 夹具：提供数据库连接
@pytest.fixture
def db_connection():
    # 假设这里是数据库连接的代码
    return "数据库连接对象"

def test_query(db_connection):
    assert db_connection == "数据库连接对象"
```

### 运行测试报告

`pytest` 支持生成详细的测试报告，可以使用 `-v` 或 `--maxfail` 等选项进行定制。

```bash
pytest -v test_module_name.py   # 显示详细的测试执行过程
pytest --maxfail=3             # 如果有 3 个测试失败，停止执行
```

## 总结

- 单元测试是确保代码质量的有效手段，`unittest` 是 Python 自带的测试框架，适用于基础的单元测试需求。
- `pytest` 是一个功能强大、易用的第三方测试框架，提供了简洁的语法、参数化测试、测试夹具等高级功能。
- 使用 `unittest` 或 `pytest` 编写并运行单元测试可以确保代码的正确性，避免潜在的错误。

通过结合使用 `unittest` 或 `pytest`，你可以在开发过程中持续进行自动化测试，提升代码质量和维护性。
