4. 车载自动化测试平台架构

Pytest 这东西,说白了就是 Python 测试界的瑞士军刀。我最早用 unittest 写测试时,每次都要写一堆 setUp、tearDown,烦得很。后来换了 Pytest,感觉整个世界都清爽了。这一章咱们就把 Pytest 的安装、配置、断言、规范、运行和报告生成,一次性讲透。

4.1 安装与配置

安装 Pytest 非常简单。我个人习惯用 pip 直接装:

pip install pytest

如果你需要生成 HTML 报告,再加一个插件:

pip install pytest-html

装完之后,验证一下是否成功:

pytest --version

嗯,这里要注意。如果你用的是虚拟环境,记得先激活环境再装。我曾经在一个项目里忘了激活虚拟环境,结果装到了全局 Python 里,折腾了半天才发现。

Pytest 的配置主要通过 pytest.ini 或 pyproject.toml 文件。我个人更推荐 pytest.ini,因为它更直观。举个例子:

[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short

这个配置告诉 Pytest:去 tests 目录下找测试文件,文件名以 test_ 开头,类名以 Test 开头,函数名以 test_ 开头。运行的时候加 -v 显示详细信息,--tb=short 让错误信息更简洁。

小技巧: 如果你团队里有人用 Windows,有人用 Mac,建议把配置写在 pyproject.toml 里,跨平台兼容性更好。

4.2 断言机制

Pytest 的断言,说白了就是用 Python 原生的 assert 关键字。你想想看,这比 unittest 的 self.assertEqual() 简洁多了吧?

基本用法:

def test_addition():
    result = 1 + 1
    assert result == 2

def test_string():
    name = "pytest"
    assert "py" in name
    assert len(name) > 0

def test_list():
    items = [1, 2, 3]
    assert 2 in items
    assert len(items) == 3

为什么 Pytest 的断言这么强大?因为它会在断言失败时,自动显示上下文信息。比如:

def test_failure():
    a = 10
    b = 20
    assert a == b

运行后你会看到类似这样的输出:

E       assert 10 == 20
E        +  where 10 = a
E        +  and 20 = b

你看,它直接告诉你 a 是 10,b 是 20,一目了然。我在项目中遇到过很多新手,断言失败后一脸懵,不知道变量值是什么。用 Pytest 就没这问题。

常用的断言场景:

场景 断言写法 说明
相等 assert a == b 判断两个值相等
不相等 assert a != b 判断两个值不相等
包含 assert x in y 判断 x 是否在 y 中
不包含 assert x not in y 判断 x 是否不在 y 中
真值 assert x 判断 x 是否为 True
假值 assert not x 判断 x 是否为 False
近似相等 assert abs(a - b) < 0.001 浮点数比较时用
避坑指南: 浮点数比较千万别直接用 assert a == b。我曾经在测试一个计算价格的函数时,就因为浮点数精度问题,导致测试时好时坏。后来改用 assert abs(a - b) < 0.001 才稳定下来。

4.3 测试用例编写规范

Pytest 的测试用例编写,有几个硬性规则。说白了,就是约定大于配置:

  • 文件名: 以 test_ 开头,比如 test_login.py
  • 类名: 以 Test 开头,比如 TestLogin
  • 函数名: 以 test_ 开头,比如 test_login_success

一个完整的测试用例示例:

# test_calculator.py

class TestCalculator:
    def test_addition(self):
        assert 1 + 1 == 2

    def test_subtraction(self):
        assert 5 - 3 == 2

    def test_multiplication(self):
        assert 3 * 4 == 12

    def test_division(self):
        assert 10 / 2 == 5

我个人习惯把测试用例按功能模块分组。比如用户模块的测试放 test_user.py,订单模块的测试放 test_order.py。这样找起来方便,维护也简单。

测试用例的命名也很重要。我建议用 test_功能_场景_预期结果 的格式。比如:

  • test_login_valid_user_should_succeed
  • test_login_invalid_password_should_fail
  • test_login_empty_username_should_show_error

你想想看,如果测试失败了,光看名字就知道是哪个场景出了问题,多省事。

核心原则: 每个测试用例只测一个功能点。如果一个测试用例里写了多个断言,一旦前面的断言失败,后面的就不会执行了。我建议一个测试用例只写一个 assert,最多不超过三个。

4.4 运行与报告生成

运行测试用例,最基础的方式就是在终端里敲:

pytest

Pytest 会自动发现当前目录及子目录下所有符合规范的测试文件并执行。如果你想指定某个文件:

pytest tests/test_login.py

常用的运行参数:

参数 作用 示例
-v 显示详细信息 pytest -v
-s 显示 print 输出 pytest -s
-k 按关键字筛选用例 pytest -k "login"
-x 遇到第一个失败就停止 pytest -x
--maxfail=n 失败 n 次后停止 pytest --maxfail=2
--tb=short 缩短错误信息 pytest --tb=short

生成 HTML 报告,需要用到 pytest-html 插件:

pytest --html=report.html

运行完后,当前目录下会生成一个 report.html 文件。打开它,你会看到所有测试用例的执行结果、耗时、错误信息等。我在项目中经常用这个报告给团队看,比在终端里翻日志方便多了。

如果你想在报告中包含截图或自定义内容,可以这样:

pytest --html=report.html --self-contained-html

--self-contained-html 参数会把所有资源(CSS、JS)都打包进一个 HTML 文件里,方便分享。

小技巧: 我习惯在 CI/CD 流程里加上 --junitxml=report.xml 参数,生成 JUnit 格式的 XML 报告。这样 Jenkins、GitLab CI 等工具可以直接解析,展示测试结果。

4.5 知识结构总览

这一章的内容比较多,我画了一张图帮你梳理一下整体脉络:

Pytest 基础入门知识结构 Pytest 核心 安装与配置 断言机制 编写规范 运行与报告 pip install pytest.ini assert 语法 上下文信息 命名规则 功能分组 命令行参数 HTML 报告 核心原则:约定大于配置,简洁优于复杂 每个测试用例只测一个功能点,命名要清晰可读

这张图把 Pytest 的四个核心模块串起来了。从安装配置开始,到断言机制、编写规范,最后是运行和报告生成。你跟着这个脉络走,就不会乱。

4.6 实战小练习

光看不练假把式。我建议你现在就动手:

  1. 创建一个 test_demo.py 文件
  2. 写三个测试函数:一个测试字符串拼接,一个测试列表排序,一个测试字典取值
  3. 运行 pytest -v 查看结果
  4. 生成 HTML 报告:pytest --html=report.html

做完这些,Pytest 的基础操作你就基本掌握了。嗯,就是这么简单。