Python 测试框架选型:unittest、pytest、nose 对比

做 SDK 验证,选对测试框架很重要。这就像盖房子选地基。地基选不好,后面全得返工。

Python 世界里,主流的测试框架就三个:unittest、pytest、nose。今天咱们把它们拉到一起,好好比一比。

1. unittest:官方正统,但有点重

unittest 是 Python 官方自带的测试框架。功能很全,但用起来有点重。我们项目初期就用的 unittest,后来发现 setup/teardown 写了一大堆,看着都烦。

举个例子,你想测一个 SDK 初始化函数。用 unittest 你得这么写:

import unittest
from my_sdk import Client

class TestClient(unittest.TestCase):
    def setUp(self):
        # 每个用例前都要初始化
        self.client = Client(="test_key")

    def test_connection(self):
        result = self.client.connect()
        self.assertTrue(result)

    def tearDown(self):
        self.client.close()

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

每个测试类必须继承 unittest.TestCase。断言方法也得用 self.assertEqual 这种形式。说实话,有点啰嗦。

2. nose:曾经辉煌,现在停产

nose 是第三方框架,比 unittest 简洁不少。它能自动收集测试用例,不用你手动 import。我在职业生涯早期用过一阵子 nose,体验确实比 unittest 好。

但有个问题——nose 已经停止维护了。大家用的都是 nose2,功能跟 nose 差不多。但社区活跃度远不如 pytest。

3. pytest:现代测试框架的标杆

pytest 是我现在的主力框架。它灵活、强大、写法简洁。你想想看,同样测试刚才的 SDK 初始化,在 pytest 里怎么写:

# test_sdk.py
import pytest
from my_sdk import Client

@pytest.fixture
def client():
    # 相当于 setUp,但更灵活
    return Client(="test_key")

def test_connection(client):
    result = client.connect()
    assert result  # 直接用 assert

不需要继承任何类。直接用 assert 做断言。fixture 比 setUp/tearDown 灵活得多。

核心对比:为什么我选 pytest

下面这张表,我建议你存下来。每次选型翻出来看看:

维度 unittest nose/nose2 pytest
社区活跃度 官方维护,但更新慢 基本停滞 ★★★★★ 极活跃
用例收集 需手动组合 TestSuite 自动收集 自动收集,规则清晰
断言写法 self.assertXxx() 支持 assert 原生 assert + 增强提示
参数化 需子测试,语法复杂 有限支持 @pytest.mark.parametrize
插件生态 极少 有限 300+ 官方插件
fixture 管理 setUp/tearDown 类似 unittest 函数式 fixture,自动清理

pytest 的核心竞争力

既然选了 pytest,咱们就深入聊聊它到底好在哪。

自动发现测试用例

你不用手动指定跑哪些文件。pytest 会自动扫描当前目录,找到所有以 test_ 开头或 _test 结尾的 .py 文件。

嗯,这里要注意:自定义测试目录时,记得配 pytest.ini:

[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*

fixture 机制,甩开 unittest几条街

我个人习惯把 fixture 理解成「依赖注入」。举个例子:

@pytest.fixture
def db_connection():
    conn = create_db_conn()
    yield conn  # 测试用
    conn.close()  # 自动清理

yield 前面的代码是 setup,后面的是 teardown。比 unittest 的 setUp/tearDown 干净多了。

避坑指南:fixture 的作用域分 function、class、module、session。默认是 function。如果你有一个耗时很长的 fixture(比如启动一个 Docker 容器),记得把 scope 设成 session,不然每个用例都跑一次,等死你。

参数化测试,覆盖所有边界

SDK 验证里,同一个接口往往要测很多组参数。pytest 的参数化写起来就很优雅:

@pytest.mark.parametrize("input_value,expected", [
    (1, 2),
    (3, 6),
    (-1, -2),
    (0, 0),
])
def test_multiply(input_value, expected):
    assert sdk_func(input_value) == expected

我在项目中遇到过漏测边界条件导致线上事故的情况。后来所有 API 都加了参数化用例,覆盖率直线上升。

插件生态,要啥有啥

pytest 的插件体系非常成熟。常用的几个:

  • pytest-xdist:并行执行用例,跑得快
  • pytest-cov:生成覆盖率报告
  • pytest-html:生成漂亮的测试报告
  • pytest-mock:更方便地 mock 外部依赖
  • pytest-timeout:给用例设置超时

我曾经遇到过测试卡住不归零的情况。加了 pytest-timeout 后,超过 30 秒的用例直接标记失败,再也不用等半天了。

结论:pytest 在灵活性、可读性、扩展性上都优于 unittest 和 nose。做 SDK 验证,选 pytest 基本不会错。我们团队从 unittest 迁移到 pytest 后,用例开发速度提升了 40%,维护成本下降了一半。

本章知识体系

Python 测试框架选型核心逻辑 Python 测试框架 unittest 官方标准 · 语法重 nose 已停止维护 pytest 现代标杆 pytest 四大核心竞争力 自动发现用例 Fixture 机制 参数化测试 300+ 插件生态
最后强调一句:选框架不能光看名气。得看团队情况、项目规模、维护成本。我个人推 pytest,但如果你团队全员只会 unittest,硬上 pytest 反而增加学习成本。不过话又说回来,pytest 真的很好上手,半天就能上手。你想想看,值不值得给团队做一次内部培训?