不写单元测试的程序员,就像光脚跑在野外——迟早要被Bug扎脚!

204 阅读6分钟

说句掏心窝子的话,写单元测试这件事,很多人知道它重要,但总是“拖一拖,再说吧” 。等到真的线上出了 Bug,老板、客户、同事都在追着问“怎么会这样?”,你才想起自己当初要是写了几行测试,可能就避免了这场灾难。

别不信,这种事身边的程序员没几个没经历过。就像有次我改了个看似无关紧要的小函数,结果上线后整个系统的订单流程挂掉了。那天晚上我坐在工位上,看着监控面板一片红,心里只剩下一个声音:完了,凉了。那一刻我才真正明白,单元测试不是锦上添花,而是救命稻草。

今天,我就带大家从头到尾走一遍 Python 单元测试的世界。放心,我不会跟你讲那些死板的定义,而是结合场景,用最接地气的方式,让你看完之后立马想动手试试。

wechat_2025-09-24_110603_116.png


一、单元测试到底是个啥?

先来个比喻:你在装修房子,每个电灯开关都要测试一下是不是通电。如果不测,等到全屋装修好才发现某个开关坏了,那修起来可是要拆墙的。

单元测试其实就是在代码层面做这种“小开关测试”。 它关心的是最小的功能单元——一个函数、一个方法是不是能正常跑。

为什么一定要写?

很多同学觉得写测试浪费时间,但真相恰恰相反。

  • 及时发现Bug:改动之后马上跑一遍测试,你能第一时间发现问题,而不是等到线上用户来帮你测。
  • 重构的安全网:你敢不敢随便优化代码?不敢吧。怕动了 A 模块,结果 B 功能也挂掉。有测试就不一样了,你能心里有底。
  • 倒逼写好代码:当你想着“这个函数我要怎么测?”时,你自然会写出逻辑更清晰、边界条件更明确的代码。
  • 团队协作更顺畅:当队友知道你写了完备的测试,他们改你的模块时会更加放心。

所以说,单元测试不是浪费时间,而是帮你省大把时间。


二、Python里的两大测试神器

说到单元测试,Python 程序员基本离不开两个框架:

  1. unittest —— Python自带的老牌框架,功能全面,官方推荐。
  2. pytest —— 社区明星框架,简洁好用,扩展能力极强。

先给大家上一段代码感受一下。

2.1 unittest:官方“正经派”

import unittest  # 导入 unittest 模块

# 被测试的函数:实现两个数相乘
def multiply(a, b):
    return a * b

# 创建测试类,继承自 unittest.TestCase
class TestMathOperations(unittest.TestCase):
    # 测试正数相乘
    def test_multiply_positive(self):
        self.assertEqual(multiply(23), 6)
    
    # 测试负数与正数相乘
    def test_multiply_negative(self):
        self.assertEqual(multiply(-13), -3)
    
    # 测试与 0 相乘
    def test_multiply_zero(self):
        self.assertEqual(multiply(0100), 0)

# 主程序入口,自动运行测试
if __name__ == '__main__':
    unittest.main()

这段代码其实很好懂:写个测试类,把各种情况(正数、负数、零)都测一遍,最后运行的时候一目了然。

2.2 pytest:简洁高效的“网红”

# test_math.py
# 被测试的函数
def multiply(a, b):
    return a * b

# 测试正数相乘
def test_multiply_positive():
    assert multiply(23) == 6

# 测试负数相乘
def test_multiply_negative():
    assert multiply(-13) == -3

# 测试 0 相乘
def test_multiply_zero():
    assert multiply(0100) == 0

运行方式:

pytest test_math.py

相比 unittest,pytest 语法更直白,没有太多“仪式感”。很多团队一旦用上 pytest,就很难回头。


三、unittest 的“深水区”:测试夹具

说到 unittest,不得不提 Fixture(测试夹具) ,听起来高大上,其实就是“测试前准备、测试后清理”。

举个例子:你要测数据库功能,总不能每次都用线上库吧?于是就有了 setUptearDown

import unittest
import sqlite3  # 导入数据库模块

class TestDatabase(unittest.TestCase):
    # 每个测试前运行:设置测试环境
    def setUp(self):
        self.conn = sqlite3.connect(':memory:')  # 创建内存数据库
        print("数据库连接已创建")

    # 每个测试后运行:清理测试环境
    def tearDown(self):
        self.conn.close()  # 关闭数据库连接
        print("数据库连接已关闭")

    # 测试数据库连接是否正常
    def test_connection(self):
        self.assertIsNotNone(self.conn)

这样就不用担心“测着测着把生产数据删了”的惨剧了。


四、Mock 测试:隔离外部依赖

你写的函数里是不是经常有这种情况:需要请求接口、访问第三方服务?要是真跑,测试就得等半天,还可能因为网络波动失败。

这时候 Mock 就登场了。

from unittest.mock import patch  # 导入 mock 工具
import requests  # 模拟 HTTP 请求

# 被测试的函数:请求接口返回数据
def get_data(url):
    response = requests.get(url)
    return response.json()

class TestAPI(unittest.TestCase):
    @patch('requests.get')  # 替换 requests.get 方法
    def test_get_data(self, mock_get):
        mock_get.return_value.json.return_value = {'key''value'}  # 模拟返回值
        result = get_data('http://fakeurl.com')
        self.assertEqual(result, {'key''value'})  # 验证返回结果

Mock 就像搭了一个假的舞台,你在上面演戏,根本不用担心现实世界出幺蛾子。


五、测试覆盖率:别只测“顺风局”

有些同学写测试,只测最简单的情况。就像打游戏只打新手村小怪,一到副本就暴露了。

所以我们需要测试覆盖率工具,比如 coverage

pip install coverage
coverage run -m unittest discover  # 运行测试并收集覆盖率数据
coverage report -m  # 生成覆盖率报告

跑完之后,你就能清楚看到哪些代码被测试覆盖到了,哪些地方还没测。


六、CI/CD:自动化才是真正的爽

写了测试还得自己点命令跑?那就太原始了。真正成熟的团队,会把测试接入到 CI/CD 流程里。

比如 GitHub Actions:

name: Python application  # 工作流名称

on: [push]  # 触发条件:代码 push 时自动执行

jobs:
  build:
    runs-on: ubuntu-latest  # 执行环境:最新 Ubuntu
    steps:
    - uses: actions/checkout@v2  # 拉取代码
    - name: Set up Python
      uses: actions/setup-python@v2  # 设置 Python 环境
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: pip install -r requirements.txt  # 安装依赖
    - name: Run tests
      run: python -m unittest discover  # 运行测试

以后你只要 push 代码,测试就会自动跑起来,出了问题第一时间通知你,爽不爽?


七、写在最后

其实程序员的世界和生活一样,你永远不知道明天和 Bug 哪个先来。

单元测试就是你的安全垫

  • 它帮你在重构时不至于心惊胆战;
  • 它让你敢于对老旧代码动刀子;
  • 它能在面试时,让你轻松拿下“你平时写测试吗?”这种问题。

别再说“写测试浪费时间”了。你写的是信心,是底气,是长期的效率提升。

从今天开始,哪怕只给关键函数写几个简单的测试,你都会发现,自己写代码的心态都不一样了。

程序员的幸福生活,从单元测试开始。🎉