返回专题首页

Python 专题

测试你的 Python 代码:pytest、fixture 与 mock 实践

Python 开发常常给人一种“改完就跑一下看看”的轻盈感,但一旦项目稍微复杂,单靠手点和临时验证是远远不够的。没有测试的项目,往往在改动积累到一定程度后就会开始畏手畏脚。

Python 专题第 24 篇 / 39 篇4 分钟

Python 开发常常给人一种“改完就跑一下看看”的轻盈感,但一旦项目稍微复杂,单靠手点和临时验证是远远不够的。没有测试的项目,往往在改动积累到一定程度后就会开始畏手畏脚。

这一节我们会用 pytest 作为主线,把最常用的测试写法、fixture 复用和 mock 隔离思路串起来,帮助你从一开始就养成可验证、可回归的开发习惯。

为什么 Python 项目更需要测试?

Python 的动态性让开发速度很快,但也意味着很多错误会更晚才暴露。

例如:

  • 某个参数结构悄悄变了;
  • 某个函数返回了 None
  • 某个外部服务超时分支没覆盖到;
  • 某段重构影响了多个调用点。

这些问题如果只靠手工点路径验证,很容易漏掉。测试的价值就在于,让一部分关键行为变成可重复执行的证据。

所以测试不是“锦上添花”,而是在项目开始变复杂时,为未来改动建立安全网。

pytest 的基本写法

pytest 之所以受欢迎,一个重要原因就是它写法简单、约定自然。

一个最基本的例子:

def add(x, y):
    return x + y


def test_add():
    assert add(1, 2) == 3

你会发现它没有很重的样板结构,核心就是:

  • 准备输入;
  • 执行行为;
  • 断言结果。

这也提醒我们,测试最重要的从来不是框架 API,而是你有没有把行为和预期说清楚。

fixture:复用测试准备逻辑

当测试数量变多时,很多准备动作会反复出现,比如创建测试用户、准备数据库连接、构造临时目录等。这时 fixture 就非常重要。

它的价值在于把“测试前置准备”抽出来统一管理,而不是在每个测试里重复复制。

作用域与依赖关系

fixture 不只是复用,它还有自己的生命周期控制。你可以根据场景决定它是每个测试都重新创建,还是整个模块、整个会话共用。

这意味着 fixture 既能帮助你减少重复,也能帮助你控制测试隔离程度和执行效率。

所以用好 fixture,本质上是在组织测试环境,而不只是少写几行代码。

mock:隔离外部依赖

真实项目里的很多测试,不适合真的去访问数据库、发 HTTP 请求或调用第三方服务。否则测试会变得慢、不稳定、难复现。

这时就需要 mock

它的核心作用,是把外部依赖替换成可控替身,让测试只聚焦当前逻辑本身。

打桩与断言调用

mock 常做两件事:

  • 打桩:让外部调用返回一个可控结果;
  • 断言:确认某个依赖是否按预期被调用。

这让你既能验证“结果对不对”,也能验证“交互过程对不对”。

不过也要注意,不要把测试写成全是 mock 的空壳。mock 应该用于隔离真实边界,而不是把所有内部实现都替换掉,导致测试完全失去真实约束。

参数化测试与边界用例设计

优秀测试的关键,不在于数量多,而在于覆盖关键边界。

pytest 的参数化能力很适合把一组相似场景组织起来,例如:

  • 合法输入;
  • 空输入;
  • 极端边界值;
  • 异常输入。

这会让测试更系统,也更容易看出“这个函数到底有哪些行为边界”。

所以写测试时,真正值得花心思的通常不是断言语法,而是你有没有想到:

  • 最正常的路径;
  • 最容易漏掉的边界;
  • 最容易出错的异常分支。

总结与预告

这一节我们不是把测试当成额外负担,而是把它还原成帮助项目持续演进的安全网。掌握了 pytestfixturemock 之后,你会更敢于重构,也更容易在问题出现时快速定位根因。

下一节我们会继续进入项目层面的组织问题,看看不同形态的 Python 项目该如何设计目录结构和模块边界。