4. 搭建自动化测试环境:Python、pytest、unittest 归纳总结

要说搭建自动化测试环境,我这些年踩过的坑还真不少。刚开始的时候,我总觉得装个 Python、写几个测试用例就完事了。结果呢?项目一复杂,环境冲突、版本不兼容、测试框架选型失误……各种问题接踵而至。今天我们就来捋一捋,到底怎么把测试环境搭得干净、可靠、可维护。

4.1 Python 环境准备——别小看这一步

Python 版本管理是第一道坎。我见过团队因为某个人在服务器上直接装了 Python 3.12,结果依赖的 some old lib 跑不起来,排查了一整天。个人建议用 pyenv 或者 conda 来隔离版本。

小提示:无论你用什么方式,虚拟环境一定要用。我习惯在每个项目根目录下建一个 .venv,然后 pip install -r requirements.txt。这样换机器也好,多人协作也好,不会乱。

举一个我最常用的初始化脚本:

# 创建虚拟环境
python -m venv .venv
# 激活(Windows)
.venv\Scripts\activate
# 激活(Mac/Linux)
source .venv/bin/activate
# 安装测试依赖
pip install pytest pytest-cov unittest-xml-reporting

有意思的是,很多人装了 pytest 后,unittest 自带的标准库就不管了。其实它俩可以共存,后面我会说怎么选。

4.2 pytest——我现在的首选

pytest 用起来是真爽。你想想看,不需要写繁琐的 TestCase 类,函数名带 test_ 开头就好了。自动发现测试文件,报错信息也清晰。

我记得刚接手一个老项目时,里面全是 unittest,参数化测试要写十几行 for 循环。我花了一个下午把核心模块改成 pytest 的 @pytest.mark.parametrize,代码量直接砍掉一半。嗯,这里要注意:pytest 可以原生运行 unittest.TestCase,迁移成本很低。

看一个简单的例子:

# test_sample.py
import pytest

def test_add():
    assert 1 + 1 == 2

@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (3, 5, 8),
    (-1, 1, 0)
])
def test_param(a, b, expected):
    assert a + b == expected

跑一下 pytest -v,结果一目了然。

我曾经踩过的坑:pytest 的 conftest.py 作用域理解错,导致 fixture 在多个测试模块间共享了不该共享的状态。建议 fixture 默认 scope="function",只在明确需要时才改成 "module" 或 "session"。

4.3 unittest——老牌稳定,但不是最优

unittest 是 Python 标准库,不用额外安装。对于中小型 SDK 回归测试,它完全够用。但语法偏啰嗦,你得写 class TestSomething(unittest.TestCase),里面方法名 test_xxx,断言用 self.assertEqual。

说实话,我现在只有两种场景会用它:
一是必须在不允许装第三方包的 CI 环境里跑测试;
二是我要验证一个遗留系统,里面全是 unittest 风格的测试,我懒得全改。

一个典型的 unittest 测试类:

import unittest

class TestMath(unittest.TestCase):
    def test_add(self):
        self.assertEqual(1 + 1, 2)

    def test_sub(self):
        self.assertEqual(2 - 1, 1)

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

4.4 框架对比——一张表说清楚

很多新人问我到底选哪个。我把核心维度列出来,你看完就明白了。

对比维度 pytest unittest
安装方式 需 pip install 标准库自带
用例编写风格 函数式 + 装饰器 面向对象,类继承
参数化测试 @pytest.mark.parametrize 原生支持 需自己写循环或用 subTest
fixture 机制 灵活,作用域可控 setUp/tearDown,粒度有限
插件生态 非常丰富(pytest-cov, xdist 等) 较少,扩展麻烦
报告可读性 详细,带颜色 diff 朴素,需额外工具
运行老 unittest 可兼容运行 原生运行

说白了,新项目我无脑选 pytest。老项目如果纯 unittest 且规模不大,我会保留它,但不推荐长时间混用。

4.5 环境搭建全流程——我推荐的步骤

下面是我自己在多个 SDK 回归测试项目中用熟的步骤,你照着走基本不会出错。

  1. 确定 Python 版本:看 SDK 官网要求,比如只支持 3.8–3.11,那就装对应版本。
  2. 创建虚拟环境:用 python -m venv .venv 或 conda create -n sdk_test python=3.10。
  3. 安装 pytes:pip install pytest pytest-cov。
  4. 可选安装 unittest-xml-reporting:如果 CI 需要 JUnit 格式报告,这个库能把 unittest 结果输出成 XML。
  5. 编写 conftest.py:放在 tests 目录下,集中管理 fixture 和钩子。
  6. 运行一次全部测试:pytest --cov=your_sdk,看看覆盖率,也验证环境通不通。
关键习惯:每次换机器或拉新代码后,先删除 .venv 重新建一个,再 pip install -r requirements.txt。我吃过依赖版本锁定的亏,之后都加 pip freeze > requirements.txt 并在 PR 中更新。

4.6 整体知识结构图

下面这张图把三大块的关系梳理了一下。看看就好,心里有个全貌。

自动化测试环境 · Python / pytest / unittest 关系图 Python 环境 pyenv / conda / venv pytest 第三方库,功能强大 unittest 标准库,稳定 pip install 标准库自带 如何选择? 新项目 / 想要高级功能 → 选 pytest 老项目 / 不能装包 → 保留 unittest pytest 可兼容运行 unittest 用例 无需重写,逐步迁移即可

4.7 避坑清单——都是真金白银换来的

  • 依赖版本锁定:一定要用 requirements.txt 锁定版本,否则某天 pytest 升级了,你的 fixture 写法不兼容,哭都来不及。
  • 不要全局安装:我吃过最亏的一次,直接在系统 Python 里 pip install,结果同一个包在两个项目里版本冲突,搞得我重装了系统环境。
  • pytest 和 unittest 混用的目录结构:建议把 unittest 风格的测试单独放在 tests/unittests/ 下,pytest 放在 tests/integration/,然后用 pytest.ini 配置 testpaths,避免扫描混乱。
  • CI 环境里不要装无关包:用 pip install -r requirements.txt --no-deps 可以避免拉入不必要的依赖。
  • Windows 上的编码问题:pytest 输出中文 log 时偶尔乱码,在 conftest.py 里加上 sys.stdout.reconfigure(encoding='utf-8') 能搞定。

以上就是我总结的 Python + pytest + unittest 环境搭建经验。记住一句话:环境搭好了,后面写测试用例才真正顺手。别一开始贪快,后面补坑的代价更大。