第4章:Pytest入门——从安装到实战

自动化测试里,选对工具很重要。Pytest 是我这几年最常用的框架,没有之一。为什么?因为它简单、灵活,而且社区生态特别好。今天我就带你从零开始,把 Pytest 装好、用起来。

4.1 安装Pytest

安装这事儿,其实一句话就能说清楚。但我还是想啰嗦两句,因为我在项目里见过太多人在这栽跟头。

pip install pytest

就这么简单。不过,我建议你用虚拟环境。为什么呢?

注意: 别直接装到系统 Python 里。我见过有人把测试框架装到系统环境,结果项目依赖冲突,折腾了一下午。用虚拟环境吧,省心。

我个人习惯这样操作:

python -m venv venv
source venv/bin/activate  # Windows 用 venv\Scripts\activate
pip install pytest

装完之后,验证一下:

pytest --version

看到版本号,就说明装好了。嗯,这里要注意:如果你在公司内网,可能需要配置代理或者使用离线包。我曾经在客户现场遇到过网络不通的情况,最后是拿着 U 盘拷的 whl 文件。

4.2 基本使用:写第一个测试

Pytest 的核心理念就一个:测试就是函数。你不需要继承什么基类,也不用写一堆注解。写个函数,以 test_ 开头,就完事了。

来看个例子:

# test_demo.py
def test_addition():
    assert 1 + 1 == 2

def test_subtraction():
    assert 3 - 1 == 2

保存文件,然后在终端运行:

pytest test_demo.py

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

============================= test session starts =============================
collected 2 items

test_demo.py ..                                                         [100%]

============================== 2 passed in 0.02s ==============================

两个点(..)代表两个测试都通过了。如果失败,会显示 F 并给出详细错误信息。

小技巧: 运行 pytest 时不加文件名,它会自动查找当前目录下所有 test_*.py*_test.py 文件。我平时开发时,直接敲 pytest 就跑完所有测试,省事。

4.3 断言机制:不只是 assert

说到断言,很多人觉得就是 assert 关键字。没错,Pytest 确实用 Python 原生的 assert。但它做了很多增强。

举个例子:

def test_list_contains():
    my_list = [1, 2, 3]
    assert 4 in my_list  # 这个会失败

运行后,Pytest 会告诉你:

E       assert 4 in [1, 2, 3]

你看,它直接把列表内容展示出来了。这在调试时特别有用。我记得有一次排查一个复杂的 JSON 比较问题,Pytest 的断言信息帮我省了半小时。

常用的断言写法:

场景 写法
相等判断 assert a == b
不等判断 assert a != b
包含判断 assert item in collection
布尔判断 assert condition
异常判断 with pytest.raises(ValueError): ...

说到异常判断,我多说两句。你想想看,测试一个函数是否抛出预期异常,其实比测试正常路径更重要。我曾经在项目中漏测了一个异常场景,结果上线后用户一输入非法数据就崩溃……从那以后,我每条测试都至少写一个异常断言。

import pytest

def divide(a, b):
    if b == 0:
        raise ValueError("除数不能为0")
    return a / b

def test_divide_by_zero():
    with pytest.raises(ValueError, match="除数不能为0"):
        divide(10, 0)
核心要点: Pytest 的断言机制,说白了就是把 Python 的 assert 包装得更智能。你不需要学什么新语法,但能得到更好的错误信息。

4.4 测试发现规则:Pytest 怎么找到你的测试

这个问题很关键。你写了一大堆测试文件,Pytest 怎么知道哪些是测试?规则其实很简单:

  • 文件名:以 test_ 开头,或以 _test 结尾。比如 test_login.pylogin_test.py
  • 函数名:以 test_ 开头。比如 test_user_login
  • 类名:以 Test 开头(注意大写 T)。比如 class TestLogin:
  • 类方法:同样以 test_ 开头

举个例子:

# test_user.py
def test_create_user():  # 会被发现
    pass

def check_user():  # 不会被发现(没有 test_ 前缀)
    pass

class TestUser:
    def test_update_user(self):  # 会被发现
        pass
    
    def helper_method(self):  # 不会被发现
        pass

运行 pytest test_user.py -v(-v 是详细模式),你会看到只发现了两个测试。

为什么会这样设计?我个人理解是:Pytest 希望你把测试代码和辅助代码分开。辅助函数不加 test_ 前缀,就不会被当成测试执行。这个设计很聪明,你想想看,如果每个函数都被当成测试,那你的工具函数怎么办?

避坑指南: 我曾经犯过一个错误——把测试类命名为 class UserTest:(注意是 Test 结尾)。结果 Pytest 没发现它。后来查文档才知道,必须是 Test 开头。嗯,这个细节很容易忽略。

如果你想自定义发现规则,可以在 pytest.inipyproject.toml 里配置。不过说实话,我建议你保持默认。团队协作时,统一的规则比花哨的配置更重要。

4.5 实战小技巧

最后,分享几个我平时常用的技巧:

  1. 只运行某个测试pytest test_file.py::test_function
  2. 按关键字筛选pytest -k "login" 会运行所有名字包含 login 的测试
  3. 显示 print 输出pytest -s,调试时特别有用
  4. 失败时停在第一处pytest -x,适合快速定位问题

我记得有一次,项目里有 2000 多个测试用例,全跑完要 10 分钟。我用 -k 参数只跑跟当前改动相关的测试,把时间缩短到 30 秒。效率提升很明显。

我的建议: 刚开始用 Pytest 时,别想着把所有功能都学会。先掌握安装、写测试、运行测试这三步。等用顺手了,再慢慢探索 fixture、参数化这些高级特性。

好了,Pytest 入门就讲到这里。下一章我们会深入 fixture,这是 Pytest 最强大的功能之一。到时候我会分享一些我在实际项目中踩过的坑,保证让你少走弯路。