4. 测试框架入门

各位同学,咱们今天聊聊Python里最经典的测试框架——unittest。

说实话,我刚开始做测试开发那会儿,市面上还没这么多花里胡哨的框架。unittest就是那个“老大哥”,稳得很。直到现在,我在很多企业级项目里,依然首选它。为什么?因为它够简单、够稳定、够原生。

1.1 unittest框架是什么?

unittest是Python标准库自带的单元测试框架。说白了,就是Python官方给你准备好的测试工具箱。你不需要装任何第三方包,直接import unittest就能用。

我个人习惯把它比作“测试界的乐高”。框架搭好了基础结构,你只需要往里面填自己的测试逻辑就行。

核心三要素:

  • TestCase类——你的测试代码要写在继承它的类里
  • 测试方法——以test_开头的方法,框架会自动识别并执行
  • 测试运行器——负责调度和执行测试的工具

我在项目中遇到过不少新手,上来就写一堆函数,然后手动调用。嗯,这样也能跑,但维护起来简直噩梦。用unittest,你的测试就有了统一的“身份证”。

1.2 TestCase类——测试的基石

TestCase是unittest的核心类。你的所有测试逻辑,都要放在继承它的子类里。

来看一个最简单的例子:

import unittest

class TestMathOperations(unittest.TestCase):
    def test_addition(self):
        result = 1 + 1
        self.assertEqual(result, 2)

    def test_subtraction(self):
        result = 5 - 3
        self.assertEqual(result, 2)

if __name__ == '__main__':
    unittest.main()

你看,就这么几行,一个完整的测试就出来了。为什么这么设计?你想想看,如果每个测试都是独立的类和方法,那管理和扩展就方便多了。

我的小建议:

每个TestCase类最好只测试一个功能模块。比如TestUserLogin只测登录,TestUserRegister只测注册。别把所有测试塞到一个类里,那样后期你会想哭的。

1.3 测试方法命名规范——名字就是文档

这一点我特别想强调。很多同学觉得命名无所谓,随便写个test1、test2就完事了。我曾经接手过一个项目,里面全是test_a、test_b这种名字……嗯,那感觉就像看天书。

unittest要求测试方法必须以test_开头。但光有这个还不够,我建议你遵循以下规范:

命名模式 示例 说明
test_功能_场景 test_login_success 测试登录成功场景
test_功能_异常 test_login_wrong_password 测试密码错误场景
test_功能_边界 test_input_max_length 测试输入最大长度边界

说白了,好的命名就是最好的注释。别人一看方法名,就知道这个测试在测什么、测什么场景。

避坑指南:

我曾经见过有人把测试方法命名为test_1、test_2……结果跑测试时,执行顺序完全不是他想的。因为unittest默认按ASCII码排序执行方法名。所以,test_10会在test_2之前执行。嗯,这坑我踩过。

1.4 测试运行器——让测试跑起来

测试写好了,怎么跑?这就轮到测试运行器登场了。

unittest提供了几种运行方式:

  • 命令行运行:python -m unittest test_module.py
  • 代码中运行:调用unittest.main()
  • 发现模式:python -m unittest discover,自动查找所有测试文件

我个人最常用的是发现模式。为什么呢?因为项目大了以后,测试文件会散落在各个目录。用discover,框架会自动帮你找到所有以test开头的文件,然后一股脑全跑了。

来看一个实际项目中的目录结构:

project/
├── tests/
│   ├── test_login.py
│   ├── test_register.py
│   └── test_logout.py
└── run_tests.py

在run_tests.py里,你可以这样写:

import unittest

if __name__ == '__main__':
    loader = unittest.TestLoader()
    suite = loader.discover('tests', pattern='test_*.py')
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

这样,不管你有多少个测试文件,一行命令全搞定。是不是很爽?

我的经验:

verbosity参数控制输出详细程度。设为0只显示结果,设为1显示点号,设为2显示每个测试方法名。我建议调试时用2,正式跑CI时用1。别问为什么,问就是日志太多CI会报警。

1.5 本章知识体系总览

为了让你更直观地理解unittest的核心结构,我画了一张图:

unittest框架核心结构 测试运行器 自动发现测试文件(test_*.py) TestSuite(测试套件) TestCase 1 test_login_success TestCase 2 test_register_fail TestCase 3 test_logout_edge 运行器 → 发现测试 → 组装套件 → 逐个执行TestCase

这张图清晰地展示了unittest的执行流程:测试运行器通过发现模式找到所有测试文件,组装成测试套件,然后逐个执行TestCase中的测试方法。嗯,就是这么个流程。

1.6 写在最后

unittest虽然看起来简单,但它是所有测试框架的根基。你以后学pytest、nose,会发现它们很多设计思路都来自unittest。所以,把这一章吃透,后面的路就好走了。

记住三个核心:TestCase是容器,test_是规矩,运行器是引擎。这三样搞明白了,unittest你就入门了。

可可精品资料库