聊聊 Python 的单元测试框架(二):nose 和它的继任者 nose2
ccwgpt 2024-10-25 10:48 37 浏览 0 评论
作者:HelloGitHub-Prodesire
一、nose
nose[1] 是一个第三方单元测试框架,它完全兼容 unittest,并且号称是一个更好用的测试框架。
那么 nose 除了具备 unittest 的所有功能外,还具有哪些优势呢?
1.1 用例编写
用例的编写方式除了编写继承于 unittest.TestCase[2] 的测试类外,还可以编写成没有继承的测试类。比如,写成如下形式也会被 nose 视作一个测试类:
from nose.tools import raises ? class TestStringMethods: ? def test_upper(self): assert 'foo'.upper() == 'FOO' ? def test_isupper(self): assert 'FOO'.isupper() assert not 'Foo'.isupper() ? @raises(TypeError) def test_split(self): s = 'hello world' assert s.split() == ['hello', 'world'] # check that s.split fails when the separator is not a string s.split(2)
当然,测试类并没有继承 unittest.TestCase,将不能使用其内置的各类 assertXXX 方法,进而导致用例出错时无法获得更加详细的上下文信息。
此外,nose 也支持定义函数来作为测试,这给许多简单的测试场景带来很大的便利:
def test_upper(): assert 'foo'.upper() == 'FOO'
1.2 用例发现和执行
unittest 所支持的用例发现和执行能力,nose 均支持。nose 支持用例自动(递归)发现:
- 默认发现当前目录下所有包含 test 的测试用例,但不包括以 _ 开头的用例
- 使用 nosetests 命令
- 通过 -w 参数指定要自动发现的目录, -m 参数指定用例文件、目录、函数、类的名称模式(正则匹配)
- nosetests -w project_directory "test_.+"
nose 也支持执行指定用例:
- 指定测试模块
- nosetests test.module
- 指定测试类
- nosetests a.test:TestCase
- 指定测试方法
- nosetests another.test:TestCase.test_method
- 指定测试文件路径
- nosetests /path/to/test/file.py
- 指定测试文件路径+测试类或测试函数(这是 unittest 所不支持的)
- nosetests /path/to/test/file.py:TestCase
- nosetests /path/to/test/file.py:TestCase.test_method
- nosetests /path/to/test/file.py:test_function
1.3 测试夹具(Fixtures)
nose 除了支持 unittest 所支持的定义测试前置和清理方式,还支持一种更为简单的定义方式:
def setup_func(): "set up test fixtures" ? def teardown_func(): "tear down test fixtures" ? @with_setup(setup_func, teardown_func) def test(): "test ..."
只需定义两个函数用来表示前置和清理方法,通过 nose.tools.with_setup[3] 装饰器装饰测试函数,nose 便会在执行测试用例前后分别执行所定义的前置和清理函数。
1.4 子测试/测试生成器
nose 除了支持 unittest 中的 TestCase.subTest,还支持一种更为强大的子测试编写方式,也就是 测试生成器(Test generators),通过 yield 实现。
在下面的示例中,定义一个 test_evens 测试函数,里面生成了 5 个子测试 check_even:
def test_evens(): for i in range(0, 5): yield check_even, i, i*3 ? def check_even(n, nn): assert n % 2 == 0 or nn % 2 == 0
此外,相较于 unittest.TestCase.subTest 多个子测试只能执行一次测试前置和清理,nose 的 测试生成器 可以支持每个子测试执行一次测试前置和清理,如:
def test_generator(): # ... yield func, arg, arg # ... ? @with_setup(setup_func, teardown_func) def func(arg): assert something_about(arg)
1.5 插件体系
nose 相较于 unittest 一个最大的优势就是插件体系,自带了很多有用的插件,也有丰富的第三方插件。这样就能做更多的事情。
其中,自带插件如下:
- AllModules[4]:在所有模块中收集用例
- Attrib[5]:给用例打标签,并可运行含指定标签的用例
- Capture[6]:捕获用例的标准输出
- Collect[7]:快速收集用例
- Cover[8]:统计代码覆盖率
- Debug[9]:用例失败时进入 pdb 调试
- Deprecated[10]:标记用例为弃用
- Doctests[11]:运行文档用例
- Failure Detail[12]:断言失败时提供上下文信息
- Isolate[13]:保护用例避免受一些副作用的影响
- Logcapture[14]:捕捉 logging 输出
- Multiprocess[15]:并行执行用例
- Prof[16]:使用热点分析器进行分析
- Skip[17]:标记用例为跳过
- Testid[18]:为输出的每个用例名称添加测试 ID
- Xunit[19]:以 xunit 格式输出测试结果
而第三方库则多种多样,如用来生成 HTML 格式测试报告的 nose-htmloutput[20] 等,这里不再一一列出。
得益于 nose 丰富的插件生态,当 nose 本身不能够完全满足我们的测试需求时,可以通过安装插件,并在 nosetests 命令行指定该插件所提供的特定参数即可非常容易的使用插件。相较于 unittest,就能省去很多自己开发额外测试逻辑的精力。
二、nose2
nose2[21] 是 nose[22] 的继任者。它们的理念都是让编写和运行测试用例变得更容易。
它们有很多相同点,比如都兼容 unittest,支持使用函数作为测试用例,支持子测试,拥有插件体系。但也有很多不同点,下面列出一些主要的不同点:
- 发现和载入测试
- nose 自行实现了模块加载功能,使用惰性方式加载测试模块,加载一个执行一个。
- nose2 则借助内建的 **import**()[23] 导入模块,并且是先全部载入,再执行用例
- nose2 并不支持 nose 所支持的所有测试用例项目结构,比如如下用例文件的结构在 nose2 中就不受支持:
. `-- tests |-- more_tests | `-- test.py `-- test.py
- 测试前置和清理函数级别
- nose 支持方法、类、模块和包级别的测试前置和清理函数
- nose2 则不支持包级别的测试前置和清理函数
- 子测试
- nose2 除了支持使用测试生成器来实现子测试外,还支持使用参数化测试(Parameterized tests)[24]来实现子测试
- nose2 除了像 nose 一样支持在测试函数和测试类(不继承于 unittest.TestCase)中支持参数化测试和测试生成器外,还支持在继承于 unittest.TestCase 的测试类中使用
- 配置化
- nose 期望所有插件的配置通过命令行参数进行配置
- nose2 则通过配置文件进行控制,以最小化命令行参数让人读得更舒服
更多对比详见 官方文档[25]。
三、小结
nose 和 nose2 在做到兼容 unittest 上就足以看出它们的目标,那便是要吸引原来那些使用 unittest 的用户来使用它们。它们确实做到了!
nose 和 nose2 在用例编写、测试夹具、子测试上做出改进,已经能让日常用例编写工作变得更加容易和灵活。同时又引入插件体系,进一步将单元测试框架的能力提升了一个大大的台阶,这让很多在基础测试功能之上的高阶功能的实现和共享成为了可能。也难怪有众多开发者对它们情有独钟。
References
[1] nose: https://nose.readthedocs.io/en/latest/
[2] unittest.TestCase: https://docs.python.org/3/library/unittest.html#unittest.TestCase
[3]nose.tools.with_setup: https://nose.readthedocs.io/en/latest/testing_tools.html?highlight=with_setup#nose.tools.with_setup
[4] AllModules: https://nose.readthedocs.io/en/latest/plugins/allmodules.html
[5]Attrib: https://nose.readthedocs.io/en/latest/plugins/attrib.html
[6]Capture: https://nose.readthedocs.io/en/latest/plugins/capture.html
[7]Collect: https://nose.readthedocs.io/en/latest/plugins/collect.html
[8] Cover: https://nose.readthedocs.io/en/latest/plugins/cover.html
[9]Debug: https://nose.readthedocs.io/en/latest/plugins/debug.html
[10]Deprecated: https://nose.readthedocs.io/en/latest/plugins/deprecated.html
[11]Doctests: https://nose.readthedocs.io/en/latest/plugins/deprecated.html
[12]Failure Detail: https://nose.readthedocs.io/en/latest/plugins/failuredetail.html
[13]Isolate: https://nose.readthedocs.io/en/latest/plugins/isolate.html
[14]Logcapture: https://nose.readthedocs.io/en/latest/plugins/logcapture.html
[15] Multiprocess: https://nose.readthedocs.io/en/latest/plugins/multiprocess.html
[16]Prof: https://nose.readthedocs.io/en/latest/plugins/prof.html
[17]Skip: https://nose.readthedocs.io/en/latest/plugins/skip.html
[18]Testid: https://nose.readthedocs.io/en/latest/plugins/testid.html
[19]Xunit: https://nose.readthedocs.io/en/latest/plugins/xunit.html
[20]nose-htmloutput: https://github.com/ionelmc/nose-htmloutput
[21]nose2: https://github.com/nose-devs/nose2
[22]nose: https://nose.readthedocs.io/en/latest/
[23]import(): https://docs.python.org/3/library/functions.html#import
[24]参数化测试(Parameterized tests): https://docs.nose2.io/en/latest/params.html#parameterized-tests
[25] 官方文档: https://docs.nose2.io/en/latest/differences.html
『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们,让更多人爱上开源、贡献开源~
相关推荐
- 一个基于.Net Core遵循Clean Architecture原则开源架构
-
今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...
- AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%
-
写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...
- OneCode低代码平台的事件驱动设计:架构解析与实践
-
引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...
- 国内大厂AI插件评测:根据UI图生成Vue前端代码
-
在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...
- AI+低代码技术揭秘(二):核心架构
-
本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...
- GitDiagram用AI把代码库变成可视化架构图
-
这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...
- 30天自制操作系统:第六天:代码架构整理与中断处理
-
1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...
- AI写代码越帮越忙?2025年研究揭露惊人真相
-
近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...
- 一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具
-
一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...
- 5分钟掌握 c# 网络通讯架构及代码示例
-
以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...
- 从复杂到优雅:用建造者和责任链重塑代码架构
-
引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...
- 低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈
-
专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...
- 框架设计并不是简单粗暴地写代码,而是要先弄清逻辑
-
3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...
- 大佬用 Avalonia 框架开发的 C# 代码 IDE
-
AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...
- 轻量级框架Lagent 仅需20行代码即可构建自己的智能代理
-
站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- 知识框架图 (52)
- ppt框架 (55)
- 框架图模板 (59)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)