一文搞懂Unittest测试方法执行顺序

简介: 一文搞懂Unittest测试方法执行顺序

大家好~我是米洛

Unittest


unittest大家应该都不陌生。它作为一款博主在5-6年前最常用的单元测试框架,现在正被pytest,nose慢慢蚕食

渐渐地,看到大家更多的讨论的内容从unittest+HTMLTestRunner变为pytest+allure2等后起之秀

不禁感慨,终究是自己落伍了,跟不上时代的大潮了。

回到主题


感慨完了,回到正文。虽然unittest正在慢慢被放弃,但是它仍然是一款很全面的测试框架

今天在群里看到番茄卷王(公众号: 测试开发番货)的一番言论,激起了我的一番回忆。

自己以前是知道unittest的执行顺序并不是按照编写test方法的顺序执行,而是按照字典序执行的。但遗憾的是我都是投机取巧去解决的问题(后面会讲)。

下面我们就来探讨下unittest类的test方法的执行顺序问题。

源码初窥


研究一下源码(unittest.TestLoader)可以发现,在加载一个class下面的test方法的时候,原生Loader进行了排序,并且根据functools.cmp_to_key方法对测试方法列表进行了排序。

1.jpg

image

我们知道,unittest是不需要我们指定对应的方法,说白了,它是从类里面自动获取到咱们的方法,并约定了以test开头的方法都会被视为测试方法。

2.jpg

可以看到testMethodPrefix,即测试方法前缀,如果不是test开头则直接return False

查询一下self.sortTestMethodsUsing(这个是一个排序的方式)。

3.jpg

找到对应的排序方法

可以看到这个比较方法写的很明确了,如果x < y那么返回-1,x = y则返回0,x > y返回1。

其实大家可能不知道Python里面的字符串也是可以比较的,在此必须说明一下字典序。我们来看看这个例子:


a = "abc"
b = "abcd"
c = "abce"
print(a > b)
print(b > c)

猜猜看执行结果,很显然,字典序的比较,是按A-Z的顺序来比较的,如果前缀一样但长度不一样,那么长度长的那个,字典序靠后。

4.jpg

所以这里面a < b < c

了解了字典序以后,我们就不难知道,在unittest里面它寻找case的过程可以这样简化:

  • 找到对应类下面以test开头的测试方法
  • 对他们进行字典序排序
  • 依次执行
    这样就不难解释为什么我们有时候写的case不按照自己想的顺序来

回到问题的本质


搞清楚为什么用例会乱,那就想到对应的解决方案。由于修改源码是不太合适的,那我们有2个策略去达成目的。

比如我有多个test方法:


class Testcase(unittest.TestCase):
    def setUp(self) -> None:
        pass
    def test_1(self):
        print("执行第一个")
    def test_2(self):
        print("第二个")
    def test_3(self):
        print("第三个")
    def test_10(self):
        print("第四个")
    def test_11(self):
        print("第五个")
    def tearDown(self) -> None:
        pass
if __name__ == "__main__":
    unittest.main()

执行起来,按照字典序,其实是1 10 11 2 3的顺序。

5.jpg

可以看到现在还是不对的

1. 以字典序的方式编写test方法


我们可以手动修改test方法的名称,这也是我早前的处理方式。也就是说把想要先执行的case字典序排到前面:


class Testcase(unittest.TestCase):
    def setUp(self) -> None:
        pass
    def test_0_1(self):
        print("执行第一个")
    def test_0_2(self):
        print("第二个")
    def test_0_3(self):
        print("第三个")
    def test_1_0(self):
        print("第四个")
    def test_1_1(self):
        print("第五个")
    def tearDown(self) -> None:
        pass

我们可以把数字按位数拆开,个位数就把10位补0,这样就能达到效果,如果会写100个case,我们就需要补2个0,比如0_0_1,当然一个文件里面也不会有太多case。

如果遇到test_login这种怎么办呢,不是数字结尾的方法。

其实是一样的,可以写成test_数字_业务的模式。番货写了一个装饰器专门解决这样的问题,大家可以去参考下。

2. 回归本质,从根本解决问题


方案1用了番货的装饰器,好是好,但是改变了方法本身的名称,我们其实可以针对他的排序方式入手,按照我们编写case的顺序排序测试方法,就能达到想要的目的。

说说思路:

  1. 手写一个loader继承自TestLoader类,改写里面的排序方法
  2. 在unittest运行的时候传入这个新的loader

来看看完整代码,注释里面写的很完善了。


import unittest
class MyTestLoader(unittest.TestLoader):
    def getTestCaseNames(self, testcase_class):
        # 调用父类的获取“测试方法”函数
        test_names = super().getTestCaseNames(testcase_class)
        # 拿到测试方法list
        testcase_methods = list(testcase_class.__dict__.keys())
        # 根据list的索引对testcase_methods进行排序
        test_names.sort(key=testcase_methods.index)
        # 返回测试方法名称
        return test_names
class Testcase(unittest.TestCase):
    def setUp(self) -> None:
        pass
    def test_1(self):
        print("执行第一个")
    def test_2(self):
        print("第二个")
    def test_3(self):
        print("第三个")
    def test_10(self):
        print("第四个")
    def test_11(self):
        print("第五个")
    def tearDown(self) -> None:
        pass
if __name__ == "__main__":
    unittest.main(testLoader=MyTestLoader())

6.jpg

执行一下还是不对

执行了一下还是不对,是不是哪里出了什么问题呢?

是因为pycharm有一种默认的unittest的调试方法,我们要改成普通的方法去执行。

7.jpg

这种就是unittest的专属测试模式

8.jpg

改成data(我的py文件名称),然后点击debug按钮

9.jpg

别选Python tests,选正常的Python

10.jpg

搞定

试试用控制台执行:

11.jpg

也没什么毛病


今天的内容就讲到这里了,看懂的记得给个赞哦~




相关文章
|
数据采集 监控 机器人
浅谈网页端IM技术及相关测试方法实践(包括WebSocket性能测试)
最开始转转的客服系统体系如IM、工单以及机器人等都是使用第三方的产品。但第三方产品对于转转的业务,以及客服的效率等都产生了诸多限制,所以我们决定自研替换第三方系统。下面主要分享一下网页端IM技术及相关测试方法,我们先从了解IM系统和WebSocket开始。
499 4
|
9月前
|
测试技术 开发者 Python
Python单元测试入门:3个核心断言方法,帮你快速定位代码bug
本文介绍Python单元测试基础,详解`unittest`框架中的三大核心断言方法:`assertEqual`验证值相等,`assertTrue`和`assertFalse`判断条件真假。通过实例演示其用法,帮助开发者自动化检测代码逻辑,提升测试效率与可靠性。
587 1
|
9月前
|
机器学习/深度学习 人工智能 自然语言处理
如何让AI更“聪明”?VLM模型的优化策略与测试方法全解析​
本文系统解析视觉语言模型(VLM)的核心机制、推理优化、评测方法与挑战。涵盖多模态对齐、KV Cache优化、性能测试及主流基准,助你全面掌握VLM技术前沿。建议点赞收藏,深入学习。
2950 8
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
1276 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
测试技术
软考软件评测师——可靠性测试测试方法
软件可靠性是指软件在规定条件和时间内完成预定功能的能力,受运行环境、软件规模、内部结构、开发方法及可靠性投入等因素影响。失效概率指软件运行中出现失效的可能性,可靠度为不发生失效的概率,平均无失效时间(MTTF)体现软件可靠程度。案例分析显示,嵌入式软件需满足高可靠性要求,如机载软件的可靠度需达99.99%以上,通过定量指标评估其是否达标。
|
消息中间件 缓存 监控
性能测试怎么做?方法、流程与核心要点解析
本文系统阐述了性能测试的核心方法论、实施流程、问题定位优化及报告编写规范。涵盖五大测试类型(负载验证、极限压力、基准比对、持续稳定性、弹性扩展)与七项关键指标,详解各阶段任务如需求分析、场景设计和环境搭建,并提供常见瓶颈识别与优化实战案例。最后规范测试报告内容框架与数据可视化建议,为企业级实践提出建立基线库、自动化回归和全链路压测体系等建议,助力高效开展性能测试工作。
|
人工智能 自然语言处理 测试技术
AxBench:斯坦福大学推出评估语言模型控制方法的基准测试框架
AxBench 是由斯坦福大学推出,用于评估语言模型可解释性方法的基准测试框架,支持概念检测和模型转向任务,帮助研究者系统地比较不同控制技术的有效性。
450 5
AxBench:斯坦福大学推出评估语言模型控制方法的基准测试框架
|
机器学习/深度学习 算法 UED
在数据驱动时代,A/B 测试成为评估机器学习项目不同方案效果的重要方法
在数据驱动时代,A/B 测试成为评估机器学习项目不同方案效果的重要方法。本文介绍 A/B 测试的基本概念、步骤及其在模型评估、算法改进、特征选择和用户体验优化中的应用,同时提供 Python 实现示例,强调其在确保项目性能和用户体验方面的关键作用。
666 6
|
JavaScript 安全 编译器
TypeScript 与 Jest 测试框架的结合使用,从 TypeScript 的测试需求出发,介绍了 Jest 的特点及其与 TypeScript 结合的优势,详细讲解了基本测试步骤、常见测试场景及异步操作测试方法
本文深入探讨了 TypeScript 与 Jest 测试框架的结合使用,从 TypeScript 的测试需求出发,介绍了 Jest 的特点及其与 TypeScript 结合的优势,详细讲解了基本测试步骤、常见测试场景及异步操作测试方法,并通过实际案例展示了其在项目中的应用效果,旨在提升代码质量和开发效率。
402 6