码农高天规范Python测试实践 原生unittest覆盖九成工程需求

【python】十分钟极速入门unittest——写测试其实很简单!一套实践就够了! — 码农高天

码农高天

开发者只要遵循测试包分离结构并选用明确的具体断言,依靠Python自带的unittest即可在零第三方依赖下以极低开销解决绝大部分代码验证与排错问题。

AI-assisted summary of 【python】十分钟极速入门unittest——写测试其实很简单!一套实践就够了! — original source. Check the original for full context.

Source published:

Key ideas

  • 规范的 Python 工程应将核心代码与测试代码分置于独立的包中,测试脚本放置于 tests 文件夹并以 test_ 开头,无需第三方依赖即可通过 python -m unittest 自动扫描并运行所有测试。 (0:00)
  • 测试断言应尽量避免盲目使用 assertTrue,改用 assertEqual 等具体断言可以在测试失败时清晰显示实际值与期望值差异,大幅降低排错成本。 (3:00)
  • 通过 unittest.skipIf 装饰器可根据操作系统或 Python 版本灵活跳过测试,同时命令行支持从模块、测试类到具体方法的三级精准执行控制。 (6:40)

Detailed notes

## Background 该视频由 YouTube 频道“码农高天”于 2024 年 4 月 3 日发布。视频简介说明,本期内容旨在介绍 Python 自带的测试框架 unittest,通过分享一套作者认为最合理的实践方案,帮助开发者从零开始快速掌握如何为自己的项目编写测试。

## TL;DR 视频系统性地梳理了使用 Python 标准库 unittest 进行单元测试的标准工程实践。作者首先演示了将业务核心代码与测试目录独立分包、配合 __init__.py 实现自动化用例发现的工程结构。随后深入讲解了继承 unittest.TestCase 的测试类编写规范,强调使用具体的 assertEqual 代替 assertTrue 以保留详细报错信息,并演示了利用上下文管理器断言异常的方法。最后,视频介绍了方法与类级别的生命周期钩子(setUp/tearDown)、@unittest.skipIf 条件跳过装饰器,以及通过命令行针对模块、类、方法三级粒度精准执行测试的实用技巧。

## Atmosphere & style 视频整体呈现出清晰明快、务实高效的极客教学风格,没有任何拖泥带水的开场,直奔代码实操与架构讲解。讲者的语调沉稳自信且充满条理,擅长以一针见血的类比(如“测试就是判卷子”)打消开发者对写测试的畏难情绪。在讲解断言与报错细节时,那种细致排雷式的工程师思维尤为突出,给人一种严谨、踏实且极易落地的专业指导感。

## Bulletin - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 规范的 Python 工程应将核心代码与测试代码分置于独立的包中,测试脚本放置于 tests 文件夹并以 test_ 开头,无需第三方依赖即可通过 python -m unittest 自动扫描并运行所有测试。 - [03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 测试断言应尽量避免盲目使用 assertTrue,改用 assertEqual 等具体断言可以在测试失败时清晰显示实际值与期望值差异,大幅降低排错成本。 - [06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) 通过 unittest.skipIf 装饰器可根据操作系统或 Python 版本灵活跳过测试,同时命令行支持从模块、测试类到具体方法的三级精准执行控制。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 推荐将核心业务代码与测试代码放在同级根目录下,均配置 __init__.py 作为标准 Python 包。 - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 测试文件命名必须以 test_ 开头(如 test_vector.py),否则 unittest 自动化测试发现机制将无法识别。 - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 在项目根目录下运行 python -m unittest 即可自动化触发所有用例的扫描和执行。 - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 测试的本质是已知输入并验证预期结果,相当于对照标准答案的判卷过程。 - [03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 测试类必须继承自 unittest.TestCase,且被执行的测试方法必须以 test_ 作为前缀。 - [03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 优先使用 assertEqual 等专用断言而非 assertTrue,前者在断言失败时能明确打印实际值,避免信息丢失。 - [03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 测试方法中一旦遇到某个断言失败,该方法后续代码将立即中止执行。 - [03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 验证异常逻辑时可使用 with self.assertRaises(ExceptionType): 上下文管理器,未按预期抛出异常时会显式报错。 - [06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) 方法级钩子 setUp 与 tearDown 分别在每个测试方法执行前后自动被调用。 - [06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) 整个测试类级别的准备与清理可通过带有 @classmethod 装饰器的 setUpClass 和 tearDownClass 实现。 - [06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) 使用 @unittest.skipIf 装饰器可根据操作系统平台(sys.platform)或解释器版本(sys.version_info)条件跳过用例。 - [06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) unittest 支持模块(Module)、测试类(Class)和具体方法(Method)三级粒度的命令行精确定位执行。 - [06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) 这套基础 unittest 实践几乎零额外开销,已足以覆盖日常工程开发中 90% 到 95% 的测试场景需求。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 工程结构与 unittest 自动化发现

许多开发者并不清楚如何为自己的代码编写基础测试,也不了解规范的工程实践结构。码农高天在本期视频中分享了一套可以直接套用的标准方案,帮助大家规范项目开发。以一个简单的二维向量库(vector)为例,推荐的项目根目录结构非常清晰:若包含应用入口可保留一个入口文件,纯库或后端项目甚至无需此入口;所有核心业务代码放入单独文件夹并配置 `__init__.py` 使其成为 Python package(包),例如 `vector/vector.py`;与核心包同级的根目录下创建 `tests` 文件夹专门存放测试,同样放置 `__init__.py` 使其变成包,其余测试脚本存放在此目录下。

选择 Python 自带的标准库 `unittest` 作为测试框架,不仅零依赖,且掌握后迁移至其他测试框架也大同小异。配置好该目录结构后,只需在项目根目录下执行 `python -m unittest`,Python 就会自动扫描并运行所有的测试用例。码农高天强调:“在写测试的时候你必须要知道正确答案它是一个判卷子的过程所以说你要知道你的代码应该做什么”。测试的本质是针对已知操作验证程序输出是否符合预期。

```mermaid flowchart TD accTitle: 项目目录结构示意图 accDescr: 展示根目录下核心业务包与测试包的标准划分

subgraph Root["项目根目录"] Entry["main.py (可选入口)"]

subgraph CorePkg["vector/ (核心包)"] Init1["__init__.py"] VectorPy["vector.py (二维向量类实现)"] end

subgraph TestPkg["tests/ (测试包)"] Init2["__init__.py"] TestFile["test_vector.py (测试脚本)"] end end ```

- 核心代码与测试代码必须通过独立的包结构隔开,均需配置 `__init__.py`。 - 测试文件名必须以 `test_` 开头(例如 `test_vector.py`),这是 `unittest` 框架能自动发现测试的硬性规则。 - 根目录运行 `python -m unittest` 即可触发自动化用例发现与执行。

---

[03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 测试用例编写与断言原则

编写测试时,首先需要导入 `unittest` 模块,并从被测包中直接导入目标类(如 `from vector import Vector`)。测试类必须继承 `unittest.TestCase`,命名通常遵循以 `Test` 开头或结尾的惯例。测试类中的每个具体测试方法必须以 `test_` 开头,否则框架将跳过该方法。在方法体中,开发者构造已知输入,并使用 `self.assert*` 系列方法来对比预期结果。

关于断言方法的选取,码农高天重点解析了为什么不能全用 `self.assertTrue`。例如验证 `v.x == 0`,虽然 `self.assertEqual(v.x, 0)` 与 `self.assertTrue(v.x == 0)` 在逻辑上等价,但断言失败时两者的表现截然不同:`assertEqual` 会清晰提示 `1!= 0`,让开发者立刻知道实际值为 1;而 `assertTrue` 只会单调地输出 `False is not true`,完全隐藏了实际的错误值。码农高天强调:“所以说当我们明确的知道要判断什么东西的时候我们应该尽量避免使用 Assert True 和 Assert False”。此外,一旦测试方法中某个断言失败,该方法后续代码将立即中止执行。

针对异常情况的测试,框架提供了 `self.assertRaises`。若被测类(如 `Vector` 的 `__init__`)在传入非数值参数时应抛出 `ValueError`,可以通过上下文管理器语法 `with self.assertRaises(ValueError):` 包裹触发异常的初始化调用。如果代码按预期抛出指定异常则测试通过;若传入合法值导致未抛出异常,测试则会以 `ValueError not raised` 明确报错。

- 测试类必须继承 `unittest.TestCase`。 - 测试方法必须以 `test_` 开头。 - 断言优先选用具体类型的 `assertEqual` 等,避免因盲目使用 `assertTrue` 导致排错信息丢失。 - 异常验证使用 `with self.assertRaises(ExceptionType):` 语法块。

---

[06:40](https://www.youtube.com/watch?v=n6DljHXv1do&t=400s) 生命周期钩子、条件跳过与精准用例执行

在实际测试流程中,经常需要在每个测试方法前后执行初始化或清理操作。`unittest` 提供了方法级的 `setUp()` 与 `tearDown()` 钩子,分别在每个测试方法执行前后自动被调用;如果需要在整个测试类级别只执行一次准备与收尾工作,则可以定义类方法 `setUpClass()` 和 `tearDownClass()`,但必须配合 `@classmethod` 装饰器使用。

另一个常见工程需求是按环境条件跳过测试。通过 `@unittest.skipIf(condition, reason)` 装饰器可以灵活控制:第一个参数是布尔表达式,结果为 `True` 时跳过;第二个参数是提示信息。常见应用场景包括限定操作系统(如 `sys.platform == "win32"` 时跳过测试),或者限制解释器版本(如 `sys.version_info < (3, 7)` 时跳过测试以要求 Python 3.7 及以上)。

```mermaid flowchart TD accTitle: unittest 粒度分层结构 accDescr: 展示模块、测试类、测试方法的三级粒度及命令行定位方式

ModuleLevel["Module Level<br/>(tests/test_vector.py)"] ClassLevel["Class Level<br/>(TestVector)"] MethodLevel["Method Level<br/>(test_init / test_add)"]

ModuleLevel --> ClassLevel ClassLevel --> MethodLevel ```

`unittest` 还支持精准运行不同层级的测试。用例被划分为三级粒度:Module 级别(测试文件)、Class 级别(测试类)以及 Method 级别(具体测试方法)。通过命令行参数可以逐级指定执行范围: - 运行模块:`python -m unittest tests.test_vector` - 运行特定类:`python -m unittest tests.test_vector.TestVector` - 运行单个方法:`python -m unittest tests.test_vector.TestVector.test_add`

码农高天总结道:“今天讲的是 unittest 的一些最最基础的功能但是就是这些最基础的功能已经涵盖了至少 90% 到 95% 的使用情况了”,并指出这套实践的开销(overhead)非常低,足以覆盖日常绝大部分工程项目的测试需求。

## Notable quotes > [03:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=180s) 当我们明确的知道要判断什么东西的时候我们应该尽量避免使用 Assert True 和 Assert False — 码农高天

> [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 在写测试的时候你必须要知道正确答案它是一个判卷子的过程所以说你要知道你的代码应该做什么 — 码农高天

## Mentioned 码农高天, Python, unittest

## Video description 这期视频介绍一下python的自带测试框架unittest。我们介绍了一套我认为最合理的用法,让你可以从零开始,迅速学会如何测试自己的项目。

[Source](https://www.youtube.com/watch?v=n6DljHXv1do)

中文简报

码农高天规范Python测试实践 原生unittest覆盖九成工程需求 开发者只要遵循测试包分离结构并选用明确的具体断言,依靠Python自带的unittest即可在零第三方依赖下以极低开销解决绝大部分代码验证与排错问题。 视频系统性地梳理了使用 Python 标准库 unittest 进行单元测试的标准工程实践。作者首先演示了将业务核心代码与测试目录独立分包、配合 __init__.py 实现自动化用例发现的工程结构。随后深入讲解了继承 unittest.TestCase 的测试类编写规范,强调使用具体的 assertEqual 代替 assertTrue 以保留详细报错信息,并演示了利用上下文管理器断言异常的方法。最后,视频介绍了方法与类级别的生命周期钩子(setUp/tearDown)、@unittest.skipIf 条件跳过装饰器,以及通过命令行针对模块、类、方法三级粒度精准执行测试的实用技巧。 规范的 Python 工程应将核心代码与测试代码分置于独立的包中,测试脚本放置于 tests 文件夹并以 test_ 开头,无需第三方依赖即可通过 python -m unittest 自动扫描并运行所有测试。 (0:00) 测试断言应尽量避免盲目使用 assertTrue,改用 assertEqual 等具体断言可以在测试失败时清晰显示实际值与期望值差异,大幅降低排错成本。 (3:00) 通过 unittest.skipIf 装饰器可根据操作系统或 Python 版本灵活跳过测试,同时命令行支持从模块、测试类到具体方法的三级精准执行控制。 (6:40)

Reading summary

码农高天提出,利用 Python 标准库 `unittest` 配合规范的目录架构与断言习惯,即可构建覆盖 90% 到 95% 日常场景的低成本测试方案。 规范项目目录与自动化用例发现 (0:00) 规范的工程结构是自动化测试的前提。针对库或后端项目,推荐将核心业务代码置于独立包(如 `vector/`),并在同级设立 `tests/` 目录。两个目录均需包含 `__init__.py` 文件以构成 Python package。为满足框架的自动识别规则,测试文件必须以 `test_` 开头命名(例如 `test_vector.py`)。测试的核心逻辑在于针对已知输入核对程序输出是否符合预期。在项目根目录下直接运行 `python -m unittest`,框架便会自动递归发现并执行所有合规的测试用例,无需引入第三方依赖。 用例编写规范与精准断言选取 (3:00) 编写具体用例时,测试类必须继承 `unittest.TestCase`,且类中的测试方法名同样必须以 `test_` 起始,否则将被框架自动跳过。在断言的选择上,码农高天建议开发者尽量避免盲目使用 `self.assertTrue` 或 `self.assertFalse`。例如在校验数值时,`self.assertEqual(v.x, 0)` 失败会给出 `1 != 0` 的明确数值差异,而 `self.assertTrue(v.x == 0)` 仅会报错 `False is not true`,遮蔽了实际排错信息。此外,一旦测试方法中某个断言失败,该方法后续代码将立即中断执行。 异常捕获机制与上下文断言 (3:00) 针对预期内抛出异常的边界场景,`unittest` 提供了 `self.assertRaises` 机制。测试可以通过上下文管理器语法 `with self.assertRaises(ValueError):` 包裹预期的错误调用。若被测逻辑如期抛出指定异常,测试便记为通过;如果传入非法输入却未触发异常,测试则会以明确的错误提示(如 `ValueError not raised`)告警,以此确保异常防御代码的可靠性。 生命周期钩子、条件跳过与粒度控制 (6:40) 框架支持不同层次的准备与清理逻辑:方法级的 `setUp()` 与 `tearDown()` 在每个测试方法前后执行,而类级别的 `setUpClass()` 与 `tearDownClass()` 需搭配 `@classmethod` 装饰器,在整个测试类执行周期内仅运行一次。通过 `@unittest.skipIf(condition, reason)`,开发者可以根据操作系统(如 `sys.platform == "win32"`)或 Python 版本(如 `sys.version_info < (3, 7)`)跳过特定测试。 在执行层面,`unittest` 将用例细分为模块、类、方法三个粒度。开发者既可在命令行直接定位到单个文件 `tests.test_vector`,也能精准运行单一测试类 `tests.test_vector.TestVector` 或具体方法 `tests.test_vector.TestVector.test_add`,从而实现调试过程中的高效验证。 码农高天认为,虽然本方案仅涉及 `unittest` 的最基础功能,但其工程开销极低且足够通用,已能覆盖绝大多数日常开发需求,同时这套核心逻辑也能无缝迁移至其他测试框架。

精炼阅读摘要

码农高天指出,针对日常 Python 项目开发,采用标准库 unittest 配合标准工程目录即可实现零依赖且低开销的自动化测试体系,该基础方案能覆盖日常绝大多数工程场景。 标准项目结构与自动化测试发现 (0:00) 规范的工程结构是测试自动化执行的前提。以二维向量库 vector 为例,项目根目录应严格将核心业务代码与测试代码拆分为独立的 Python 包。业务核心代码存放在单独文件夹内,并通过包含 __init__.py 形成 package,例如 vector/vector.py;测试脚本则统一归入同级的 tests 文件夹,同样必须放置 __init__.py 使其成为包。测试文件的命名必须遵循以 test_ 开头的规则,例如 test_vector.py。在根目录执行 python -m unittest 命令时,框架会自动扫描并运行所有符合规范的测试用例。码农高天强调,写测试本质上是基于已知输入验证预期输出的“判卷”过程,因此必须预先明确代码应有的执行结果。 测试类设计与断言编写规范 (3:00) 编写具体测试用例时,测试类必须继承 unittest.TestCase,且类内的各个测试方法名必须以 test_ 开头,否则会被框架直接跳过。断言的选取对排错效率具有关键影响。码农高天强调,在有明确预期值时应尽量避免直接使用 assertTrue 或 assertFalse,而应优先选择 assertEqual 等具象断言方法。例如验证 v.x == 0 时,assertTrue 失败仅输出 False is not true,隐藏了实际错误数值;而 assertEqual 会明确指出 1 != 0,让排错一目了然。此外,一旦方法内某个断言失败,该方法后续代码将立刻终止执行。针对异常抛出的验证,框架提供了 with self.assertRaises(ExceptionType): 上下文管理器,若目标代码未按预期抛出指定异常,测试会提示错误未触发并判定失败。 生命周期钩子与精准执行控制 (6:40) 为了满足测试环境的准备与清理需求,unittest 提供了分层级的生命周期钩子。方法级别的 setUp() 和 tearDown() 会在每个测试方法执行前后自动触发;若需在整个测试类维度仅执行一次准备与收尾操作,则可借助类方法 setUpClass() 与 tearDownClass(),两者均需配合 @classmethod 装饰器使用。针对不同平台或运行环境的差异,可以使用 @unittest.skipIf(condition, reason) 装饰器,当布尔条件为真时跳过执行,例如指定 sys.platform == 'win32' 或 Python 解释器版本低于 3.7 时跳过该项测试。在命令行运行层面,测试体系划分为模块、测试类、测试方法三级粒度,开发者既可按整个模块 tests.test_vector 运行,也可以精确指定特定类 tests.test_vector.TestVector 或具体方法 tests.test_vector.TestVector.test_add 单独执行。 码农高天认为,视频所涵盖的 unittest 基础功能开发开销极低,但足以支撑日常项目中 90% 到 95% 的测试场景需求,熟练掌握后也能轻松迁移至其他测试框架。

Detailed notes — zh

## Background 本视频由 YouTube 频道「码农高天」于 2024 年 4 月 3 日发布。视频简介指出,本期内容旨在介绍 Python 自带的测试框架 unittest,通过分享一套作者认为最合理的实践方案,帮助开发者从零开始快速掌握如何为自己的项目编写测试。 ## TL;DR 视频首先讲解了将核心源码与测试代码分包隔离的标准工程目录结构,演示了通过终端命令快速触发自动测试发现的方法。接着,作者以二维向量类为例,梳理了测试用例与方法的命名规范,并强调选用更具诊断价值的具体断言方法及使用上下文管理器捕获预期异常。最后,视频进一步介绍了利用固件钩子管理生命周期、基于平台或版本实现条件跳过,以及按模块、类、方法多层级精确执行测试的具体技巧。 ## Atmosphere & style 视频风格务实干练、直击痛点,讲解节奏紧凑而条理分明。主讲人以清晰的工程师思维拆解规范,将写测试生动比喻为「判卷子」,语言通俗且充满启发性。整体观感轻松流畅且极具实操价值,能够有效消除初学者对编写工程测试的畏难情绪。 ## Bulletin - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 规范的项目结构推荐将业务源码与测试代码物理隔离为独立包,在项目根目录下通过执行 python -m unittest 即可实现测试用例的零配置自动扫描与执行。 - [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 测试文件和测试方法必须严格遵循以 test_ 开头的命名约定,类需继承自 unittest.TestCase,断言时应优先使用 assertEqual 等具体方法以输出详细的报错差量信息。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 利用 setUp、tearDown 及测试类级别的类方法可以精确管理环境生命周期,结合 @unittest.skipIf 装饰器还能轻松实现多版本或跨平台的条件测试。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 开发者无需每次都运行全量测试,通过指定“包.模块.测试类.测试方法”路径即可实现细粒度的单点调试,覆盖绝大多数日常工程场景。 ## Key takeaways - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 推荐的标准项目结构包含独立的业务源码包(如 vector)与测试包(test),均需放置 __init__.py 声明为 Python package。 - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 使用 Python 标准库自带的 unittest 框架无需安装任何第三方依赖,即可获得完整的自动化测试能力。 - [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 在规范目录搭建完成后,在项目根目录运行 python -m unittest 即可自动发现并运行所有用例。 - [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 写测试本质上是用已知输入核对实际输出的“判卷”过程,要求开发者必须明确代码在各种输入下的预期行为。 - [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 测试文件命名必须以 test_ 开头,测试类必须继承 unittest.TestCase,类内部的测试方法也必须以 test_ 开头方能被框架识别。 - [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 断言编写应当避免泛用 assertTrue,优先选用如 assertEqual 等具体断言,以便在测试失败时直接输出实际值与期望值的差异。 - [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 测试方法在遇到首个断言失败时会立即中断退出,不再继续执行该方法内的后续断言。 - [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 验证异常处理逻辑时,推荐使用 with self.assertRaises(ExceptionType): 上下文管理器,未按预期抛出异常则判定测试失败。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 通过定义 setUp 和 tearDown 钩子方法,可以在每个测试方法执行前后分别运行通用的初始化与清理操作。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 使用修饰了 @classmethod 的 setUpClass 和 tearDownClass,能够在整个测试类运行前后仅分别执行一次生命周期操作。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 使用 @unittest.skipIf(condition, reason) 装饰器,可以根据操作系统或 Python 版本等布尔条件灵活跳过特定测试用例。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) unittest 支持从模块、测试类到单个测试方法的层级化运行参数,方便本地单点排查与高效调试。 - [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 掌握 unittest 的基础功能已足以覆盖 90% 到 95% 的实际开发测试需求,具有极低的学习成本与高实用价值。 ## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 工程结构与 unittest 自动化发现 许多开发者并不清楚如何为自己的代码编写基础测试,也不了解规范的工程实践结构。码农高天建议采用一套简洁且可以直接套用的项目文件组织方式:项目根目录下通常只需要一个入口文件;如果开发的是公共包或者某些后端服务,甚至连这个入口文件都可以省略。核心业务逻辑应当统一存放在独立文件夹中(例如本例中的 `vector` 文件夹),并通过在该目录下放置 `__init__.py` 将其声明为一个 Python package(包)。这个 `__init__.py` 可以暴露出模块接口,也可以保持完全空白;核心实现代码则放在子文件(如 `vector.py`)中。 为了组织测试代码,根目录下需要单独建立一个名为 `test` 的文件夹,并同样在其中放置 `__init__.py` 使其成为一个 package。所有具体的测试代码文件都放置在 `test` 目录内。码农高天指出,测试框架选用 Python 标准库自带的 `unittest`:“我本人是非常喜欢用标准库的,所以一直在用 unittest。大家先把 unittest 学明白了以后,如果你想用其他的 framework 也差不多。”在按照这套规范搭建好目录之后,开发者完全无需手动逐个调用测试脚本,只需在项目根目录下执行 `python -m unittest`,Python 就会自动扫描并运行所有的测试用例。 ```mermaid flowchart TD accTitle: 项目工程与测试目录结构 accDescr: 展示根目录下核心源码包与测试包的组织关系及 unittest 扫描流程 subgraph Root["项目根目录"] Entry["入口文件 (可选)"] subgraph CorePkg["vector/ 源码包"] Init1["__init__.py"] CoreCode["vector.py (二维向量类实现)"] end subgraph TestPkg["test/ 测试包"] Init2["__init__.py"] TestCode["test_vector.py (测试用例)"] end end Cmd["终端运行: python -m unittest"] --> TestPkg TestPkg -->|"自动发现并运行"| TestCode TestCode -->|"from vector import Vector"| CorePkg ``` - 核心代码与测试代码必须通过独立的包进行物理隔离,测试目录约定命名为 `test`。 - 测试框架选用内置的 `unittest`,无需额外安装第三方依赖即可实现测试的自动发现与执行。 --- ### [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 测试编写规范与断言方法的选用 从本质上讲,编写测试就是拿已知输入去验证运行结果是否符合预期,码农高天形象地将其比喻为一个判卷子的过程:“在写测试的时候你必须要知道正确答案,它是一个判卷子的过程,所以说你要知道你的代码应该做什么。”视频中以一个包含初始化、向量加法、数乘、点乘和取模操作的简单二维向量类(`Vector`)为例,演示测试代码的具体编写步骤。 在 `unittest` 中,测试文件和内部测试方法的命名受到严格约束: 1. 测试文件名必须以 `test_` 开头(例如 `test_vector.py`),否则框架无法自动识别。 2. 测试类必须继承自 `unittest.TestCase`,命名习惯上通常以 `Test` 开头或结尾。 3. 测试类内部的方法必须以 `test_` 开头,若未遵循此命名规范,`unittest` 将直接忽略该方法。 在测试方法体内,需要使用以 `self.assert` 开头的一系列断言函数来核对真实输出与理论结果。针对很多初学者喜欢处处使用 `self.assertTrue` 的情况,码农高天强调应当优先使用针对性的断言方法(如 `self.assertEqual`)。例如判断 `v.x` 是否为 `0` 时,虽然 `self.assertTrue(v.x == 0)` 和 `self.assertEqual(v.x, 0)` 在逻辑上等价,但报错体验完全不同:一旦用例失败,`assertTrue` 仅仅会输出毫无诊断价值的“False is not True”;而 `assertEqual` 则会明确提示“1!= 0”,直接展示出变量当前的实际值。 此外,每个测试方法在遇到首个断言失败时就会立刻中断退出,不会继续执行后续断言。当测试目标是验证非法参数是否会触发异常时(例如向向量传入非数值类型需抛出 `ValueError`),可以使用上下文管理器 `with self.assertRaises(ValueError):`。在该代码块内执行引发异常的操作,如果代码按预期抛出异常则测试通过;若传入合法值导致没有异常抛出,测试便会失败并提示“ValueError not raised”。 - 测试文件及类内部的方法均必须以 `test_` 作为前缀命名。 - 应当尽量避免使用 `assertTrue` 和 `assertFalse` 来比较数值或状态,优先使用 `assertEqual` 等具体断言以获取详尽的错误差量信息。 - 验证异常抛出时,使用 `with self.assertRaises(ExceptionType):` 上下文结构包裹被测逻辑。 --- ### [06:47](https://www.youtube.com/watch?v=n6DljHXv1do&t=407s) 固件钩子、条件跳过与指定层级运行 随着测试规模扩大,`unittest` 提供了多种控制生命周期与执行范围的高级特性。首先是固件(Fixture)钩子:如果需要在每个测试方法运行前后执行特定的前置准备与后置清理,可以在测试类中定义 `setUp` 和 `tearDown` 方法;如果希望在整个测试类启动前和全部方法结束之后仅执行一次操作,则可以使用 `@classmethod` 装饰器修饰的 `setUpClass` 与 `tearDownClass`。 针对跨平台或多 Python 版本的适配测试,`unittest.skipIf` 装饰器非常实用。该装饰器接收两个参数:第一个是布尔表达式,当条件计算为 `True` 时跳过执行;第二个是跳过原因的说明信息。常见的用法包括: - 通过 `sys.platform == "win32"` 排除在 Windows 系统下的运行。 - 通过 `sys.version_info < (3, 7)` 限制该测试仅在 Python 3.7 及以上版本执行。 在实际开发调试中,往往不需要每次都跑全量测试,`unittest` 支持命令行精确指定三级粒度: 1. **模块级别(Module level)**:运行单个测试文件,例如 `python -m unittest test.test_vector`。 2. **类级别(Class level)**:运行某个文件中的特定测试类,例如 `python -m unittest test.test_vector.TestVector`。 3. **方法级别(Method level)**:精准定位并仅执行某一个测试方法,例如 `python -m unittest test.test_vector.TestVector.test_add`。 码农高天总结道,虽然本期内容只涵盖了框架的基础知识,但实用性极高:“我们今天讲的是 unittest 的一些最最基础的功能,但是就是这些最基础的功能已经涵盖了至少 90% 到 95% 的使用情况了”,开发者完全可以借此快速规范自己的项目,而不需要承担过高的学习和配置成本。 - `setUp` / `tearDown` 在每个测试方法前后均执行一次;`setUpClass` / `tearDownClass` 针对每个测试类仅执行一次且必须加 `@classmethod`。 - `@unittest.skipIf(condition, reason)` 用于根据平台环境或 Python 版本有条件地跳过特定测试。 - 命令行运行参数支持 `包.模块.测试类.测试方法` 的精准路径过滤,大幅提升单点调试效率。 ## Notable quotes > [02:30](https://www.youtube.com/watch?v=n6DljHXv1do&t=150s) 在写测试的时候你必须要知道正确答案,它是一个判卷子的过程,所以说你要知道你的代码应该做什么。 — 码农高天 > [00:00](https://www.youtube.com/watch?v=n6DljHXv1do&t=0s) 我本人是非常喜欢用标准库的,所以一直在用 unittest。大家先把 unittest 学明白了以后,如果你想用其他的 framework 也差不多。 — 码农高天 ## Mentioned 码农高天, Python, unittest, Windows ## Video description 这期视频介绍一下python的自带测试框架unittest。我们介绍了一套我认为最合理的用法,让你可以从零开始,迅速学会如何测试自己的项目。 [Source](https://www.youtube.com/watch?v=n6DljHXv1do)

Source quotes

[2:30] 码农高天: 在写测试的时候你必须要知道正确答案,它是一个判卷子的过程,所以说你要知道你的代码应该做什么。 [0:00] 码农高天: 我本人是非常喜欢用标准库的,所以一直在用 unittest。大家先把 unittest 学明白了以后,如果你想用其他的 framework 也差不多。

Source quotes

[3:00] 码农高天: 当我们明确的知道要判断什么东西的时候我们应该尽量避免使用 Assert True 和 Assert False [0:00] 码农高天: 在写测试的时候你必须要知道正确答案它是一个判卷子的过程所以说你要知道你的代码应该做什么

Original transcript

[0:00] 这期视频我们讲一下测试我发现很多人不知道如何对自己的代码完成一个比较基础的测试或者说他们可能不知道一个比较好的工程实践是什么样的所以这次我们提供一个可以直接超过去用的实践方式来帮助大家完成一个更正规的编程项目废话不多说 我们直接开始首先呢讲一下文件结构我们看左边我推荐的文件结构是这样的在根目录下只需要一个入口文件即可那如果你是开发一些包或者某一些后端的话连这个入口文件都可以不要这次我们的例子是写了一个非常简单的二维向量的库你所有的核心代码都应该放到一个文件夹里在这个例子里我们放到vector这个文件夹里我们要把这个文件夹变成一个package也就是说这个文件夹里面我们需要这个init.py这个init.py里面呢可以放一些接口也可以压根什么都没有这个vector文件夹入里面的vector.py就是我们核心代码保存的地方当然这个文件夹里面可能有很多文件对吧不同的功能在根目录下放一个叫做test的文件夹这里面专门放我们的测试同样的测试文件夹也把它变成一个package也就是里面放一个init.py这个以那点派理可以什么都没有这个文件夹里面其他的文件就都是我们写的测试文件我们这次给大家讲的测试的框架是unit test是python自带的一个测试框架我本人是非常喜欢用标准库的所以一直在用unitest大家先把unitize学明白了以后如果你想用其他的framework也差不多当你像我介绍的这样把项目整理好之后你只需要在根目录下运行Python 格่า อ่าม UnitestPython就会自动去寻找你的测试并且运行测试我们可以看到这里他运行了一个测试然后没有任何测试fail了我们接下来就介绍一下测试怎么写给大家一些基础的知识让你可以给自己的项目写测试首先,测试是什么?测试是检查你程序的运行结果是不是你的期待结果换言之在写测试的时候你必须要知道正确答案它是一个判卷子的过程所以说你要知道你的代码应该做什么那我们来看一下我们的原代码我们写的是一个非常简单的二维向量类在initialization的时候传一个x传一个y然后我们做了向量的加法 向量的竖程 向量的点程 还有个向量的膜那这些内容都很简单我们不赘述啊我们这期视频的核现在是谈测试好 我们看回我们的测试文件夹首先每一个测试文件必须要是Test下滑线开头就是他必须得叫TES下滑点什么东西点π这个是UNITES本身的规定啊在测试文件里 我们要干这么几件事首先我们要把这个Unitask import进来因为我们需要继承它的test case这个类第二 把我们要测试的内容给impro进来之前为什么我强调把它做成一个package因为这样的话你在import的时候会比较方便你可以直接做 from vector import vector我们就把这个Vector Lay给imput进来了接下来就是写test class这个test class首先必须要继承这个unit test 里面的test case 这个类其次一般来说这个class是test开头或者test结尾这是一个习惯问题每一个test class里面可以有若干个test method而这些test method必须以test下方键开头否则 Unit Test 会检测不到在每一个test method里面,我们就要进行正式的测试了所谓测试就是我做一个我已经知道结果的事情然后验证得到的结果是不是我已经知道的结果比如说当我们新建了一个vector然后把1 2给当作argument传进去的时候这个vector里面的X和Y这两个attribute应该分别被复制成1和2而在unitess里面有一组去检验我们实际得到的结果是不是理论结果的函数它们都是self.azert开头的比如在这里我们就用到了 self.azard equal那如果你打开python unit test的官方文档你可以看到这里面有很多的azard什么的函数当然这些只是相对常用的 还有很多没有那么常用的 [5:00] 比如说这些那当你想判断一个结果的时候 首先要查一下unitest官方有没有给出这种结果的判断方式好 那肯定有人会问 既然他提供了azert true这个方式 为什么我们不所有的地方都用azert true呢比如说这里我们用azer=v.x0和azer=v.x=0它们俩是等价的那这里我们之所以要用azer equal是因为azer equal在fail的时候可以给出更多的信息我们看在这个例子里面v.x显然是1对吧那么这两个东西都会fail掉如果这里是azer equal fail掉,它会告诉你1不等于0,你就知道v.x是1了这里注意一下每个test method当它有一个地方fail的时候它就不会继续往下运行了假设我们用other true的话他只会跟你说false is not true我们只知道v.x不是0 但我们不知道它是什么所以说当我们明确的知道要判断什么东西的时候我们应该尽量避免使用Azert True和Azert False这里我们要单独拿出来说一下是Azorbe Raises这里比如说我们把这个vector的内弹数改一下我们首先要确认这个xy必须要是数才可以否则的话我们raise一个value error这个时候我们想测试我们这个Init函数是不是成功的race了这个Error那我们的写法就是with self-assert races然后把这个exception放进去在这个width里面去做raise这个exception的事我们可以看到右边我们运行一下这个Unity Test它就Pass了因为我们传进去的两个值不是正常的value相反的如果我们传取值是合法值的话那么这个测试就会fail他只会写Value Error Not Raised好 接下来我们来介绍几个非常常用的Unitest的一些feature首先我们有可能会在运行每一个test method之前或者之后做一些事我们举个最简单的例子 假设我们在运行每一个测试之前之后都要打印这个测试开始了 这个测试结束了那我们可以在这个test class之内通过定义setup函数和teardown函数来完成我们看在setup函数里面我们打印了一个start然后在tele down函数里面我们打印了一个end于是我们在运行Unit Test的时候输出里面就会有个Start跟一个End那如果我们又增加了一个test method,就可以看到start跟end被打印了两次如果我们想每一个test class运行之前和结束之后做一些事的话我们用到的函数就是setup class跟teardown class注意这两个东西必须要用class method这个装饰器装饰一下那还有一个很常见的需求呢就是我希望我的测试在某些情况下不运行这时候我们就要用到一个Decorator,是unittest.skipif我们看这里,这个Decorator take两个argument第一个是一个bullying在这个boolean evaluate 成 true 的时候 这个测试就不运行那第二个呢是一个message在这个例子里面我测试了一下如果我Sys.platform是Win32的话就不运行这个测试也就是这个测试不在windows上运行还有一个比较常见的条件就是version info就是python的版本比如这里我写的sys.version.info小于3.7就是说它只支持3.7级以上的版本好 最后我们介绍一下怎么运行指定的测试我们现在所有的测试被分成了三级第1个是module level 也就是某个文件 比如说test下方线vector.pi在一个文件里面我们可能有若干个test class比如说这个testVectorClass在每一个test class里面我们可能有若干个test method比如说我们现在的test-init跟test-add我们可以通过控制Unitas command line的argument来告诉 UnitTest运行哪些Test比如在这里我们运行的是 test 这个文件夹下的 test vector 这个 module 里面的 test vector 这个 class 里面的 test add 这个 method可以看到他只运行了一个test那如果我们把条件放宽一些我们运行test这个文件夹里面的test vector这个file他就会运行两个test了我们今天讲的是Unitest的一些最最基础的功能但是就是这些最基础的功能已经涵盖了至少90%到95%的使用情况了也就是说对于你自己 [10:00] 像这一套流程已经基本足够用了你应该可以感受到给自己的项目写测试其实不是一个非常费劲的事儿它的overhead还是挺低的好 那这期视频就到这里 希望对大家有所帮助 喜欢的话记得点下关注 别忘了一键三零哦字幕志愿者 杨茜茜