4、自动化测试框架:unittest框架入门、pytest框架入门、如何选择框架

说到自动化测试框架,很多刚入行的朋友会问我:「到底该学哪个?」

我的回答通常是:先别急着选,把两个都摸一遍。就像选工具,你得知道扳手和螺丝刀各自擅长什么。

这一章,我就带你快速上手 unittest 和 pytest,再聊聊我这些年选框架的一些心得。

4.1 unittest 框架入门

unittest 是 Python 自带的测试框架,不用装任何第三方库。说白了,它是「官方标配」。

我个人习惯在写小型测试脚本或者给客户做演示时用 unittest,因为环境干净,拿来就用。

4.1.1 核心概念

  • TestCase:测试用例类,你写的每个测试都继承它
  • TestSuite:测试套件,用来组合多个用例
  • TestRunner:测试运行器,负责执行并输出结果
  • Fixture:setUp / tearDown,测试前后的准备和清理工作

4.1.2 一个简单的例子

假设我们要测试一个 MIL 仿真中的信号校验函数:

import unittest

def check_signal_range(value, min_val, max_val):
    """检查信号是否在合理范围内"""
    return min_val <= value <= max_val

class TestSignalCheck(unittest.TestCase):

    def setUp(self):
        # 我在项目中习惯在这里加载仿真数据
        self.signal_name = "EngineSpeed"
        print(f"准备测试信号: {self.signal_name}")

    def test_normal_range(self):
        result = check_signal_range(1500, 0, 3000)
        self.assertTrue(result)

    def test_out_of_range(self):
        result = check_signal_range(3500, 0, 3000)
        self.assertFalse(result)

    def tearDown(self):
        # 清理工作,比如关闭文件句柄
        print(f"测试完成: {self.signal_name}")

if __name__ == '__main__':
    unittest.main()
我的小技巧: 在 setUp 里只放公共的初始化代码。如果每个测试需要不同的数据,我建议在测试方法内部单独准备,避免耦合。

4.1.3 常用断言方法

断言方法 用途
assertEqual(a, b) 判断 a == b
assertTrue(x) 判断 x 为 True
assertFalse(x) 判断 x 为 False
assertIn(a, b) 判断 a 在 b 中
assertAlmostEqual(a, b) 判断浮点数近似相等(MIL 测试中常用)
注意: 在 MIL 测试中,浮点数比较一定要用 assertAlmostEqual,否则会因为精度问题导致测试误报。我曾经因为这个坑排查了整整一个下午。

4.2 pytest 框架入门

pytest 是第三方框架,但它现在几乎成了 Python 测试的事实标准。为什么?因为它写起来更简洁,功能也更强大。

我第一次用 pytest 时,最大的感受是:代码量少了一半。你想想看,同样的测试逻辑,少写一半代码,谁不爱?

4.2.1 安装与基本用法

pip install pytest

安装完就能用。pytest 会自动发现当前目录下所有以 test_ 开头的文件和函数。

4.2.2 用 pytest 重写上面的例子

import pytest

def check_signal_range(value, min_val, max_val):
    return min_val <= value <= max_val

def test_normal_range():
    assert check_signal_range(1500, 0, 3000) == True

def test_out_of_range():
    assert check_signal_range(3500, 0, 3000) == False

看到了吗?不需要继承,不需要 setUp/tearDown,直接写函数就行。

4.2.3 Fixture 的妙用

pytest 的 fixture 比 unittest 灵活得多。它可以共享数据,还可以自动管理作用域。

import pytest

@pytest.fixture
def signal_data():
    # 准备测试数据
    data = {"name": "EngineSpeed", "value": 1500, "min": 0, "max": 3000}
    yield data
    # 测试结束后清理
    print("清理信号数据")

def test_signal_in_range(signal_data):
    assert signal_data["min"] <= signal_data["value"] <= signal_data["max"]
为什么我偏爱 pytest? 因为它的 fixture 可以跨模块复用。在 MIL 测试中,我们经常需要加载同一个仿真模型,用 pytest 的 conftest.py 就能全局共享,省事很多。

4.2.4 参数化测试

这是 pytest 的杀手锏。一个测试函数,跑多组数据。

import pytest

@pytest.mark.parametrize("value, min_val, max_val, expected", [
    (1500, 0, 3000, True),
    (3500, 0, 3000, False),
    (0, 0, 3000, True),
    (-1, 0, 3000, False),
])
def test_signal_range(value, min_val, max_val, expected):
    assert check_signal_range(value, min_val, max_val) == expected

一行代码,四组测试。这在 unittest 里得写四个方法。

4.3 如何选择框架

这个问题没有标准答案。但我可以给你一些参考,都是我在实际项目中踩过的坑换来的经验。

4.3.1 选 unittest 的场景

  • 环境受限:客户现场不能装第三方库,只能用 Python 自带库
  • 团队习惯:老团队一直用 unittest,迁移成本高
  • 简单脚本:就几个测试用例,没必要引入 pytest

4.3.2 选 pytest 的场景

  • 测试量大:成百上千个测试用例,参数化能省大量代码
  • 需要插件:比如 pytest-html 生成报告,pytest-xdist 并行执行
  • 持续集成:pytest 和 Jenkins、GitLab CI 集成更顺畅

4.3.3 我的建议

如果你刚开始做 MIL 测试自动化,我建议从 pytest 入手。原因很简单:

  1. 学习曲线平缓,半小时就能上手
  2. 社区活跃,遇到问题容易找到答案
  3. 扩展性强,后期加功能不用重构
一句话总结: unittest 是「够用」,pytest 是「好用」。在 MIL 测试中,我几乎只用 pytest,除非客户有特殊要求。
避坑指南: 我曾经在一个项目中,因为用了 unittest 的 TestSuite 手动组织用例,结果维护起来痛苦不堪。后来换成 pytest 的自动发现机制,世界清净了。所以,能用自动发现就别手动组织。

好了,这一章就到这里。下一章我会带你写一个完整的 MIL 测试自动化脚本,把今天学的框架用起来。