Pytest 框架执行用例流程浅谈

背景:

  根据以下简单的代码示例,我们将从源码的角度分析其中的关键加载执行步骤,对pytest整体流程架构有个初步学习。

代码示例:

import pytest

def test_add():

assert 1 + 1 == 2

def test_sub():

assert 2 - 1 == 1

  通过 pytest test_example.py 运行此代码示例后,会触发pytest的入口函数main(),这个函数定义在src/pytest/__main__.py中,它的作用是创建一个PytestConfig对象,并调用其

do_configure()和do_main()方法。PytestConfig对象是pytest的核心配置类,它负责解析命令行参数、读取配置文件、注册插件、创建Session对象等。PytestConfig对象定义在

src/_pytest/config/__init__.py中,它继承了pluggy.HookimplMarker类,也就是说它可以作为一个插件管理器,调用各种hook函数。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

```python

# src/pytest/__main__.py

def main():

    # 创建PytestConfig对象

    config = PytestConfig()

    # 调用config.do_configure()方法

    config.do_configure()

    # 调用config.do_main()方法

    config.do_main()

```

```python

# src/_pytest/config/__init__.py

class PytestConfig(pluggy.HookimplMarker):

    def __init__(self):

        # 解析命令行参数

        self.parse_args()

        # 读取配置文件

        self.read_config_files()

        # 注册插件

        self.register_plugins()

        # 创建Session对象

        self.session = Session(self)

     

    def do_configure(self):

        # 调用hook函数pytest_configure

        self.hook.pytest_configure(config=self)

     

    def do_main(self):

        # 调用hook函数pytest_sessionstart

        self.hook.pytest_sessionstart(session=self.session)

        # 调用Session对象的main()方法

        self.session.main()

```

  Session对象是pytest的核心上下文类,它负责管理整个测试过程的信息,包括收集测试用例、执行测试用例、生成测试报告等。Session对象定义在src/_pytest/main.py中,它继承了

Collector类,也就是说它可以作为一个测试用例收集器。Session对象的main()方法是执行测试用例的主要入口,它会调用perform_collect()方法来收集测试用例,并返回一个列表items;然后

调用runtestloop()方法来循环执行items中的每个Item对象;最后调用hook函数pytest_sessionfinish来结束测试会话,并返回一个退出码exitstatus。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

```python

# src/_pytest/main.py

class Session(Collector):

    def __init__(self, config):

        # 初始化一些属性和状态信息

     

    def main(self):

        # 调用perform_collect()方法收集测试用例,并返回items列表

        items = self.perform_collect()

        # 调用runtestloop()方法循环执行items中的每个Item对象,并返回退出码exitstatus

        exitstatus = self.runtestloop(items)

        # 调用hook函数pytest_sessionfinish来结束测试会话,并返回退出码exitstatus

        self.hook.pytest_sessionfinish(session=self, exitstatus=exitstatus)

        return exitstatus

     

    def perform_collect(self):

        # 调用hook函数pytest_collectstart表示开始收集测试用例

        self.hook.pytest_collectstart(collector=self)

        # 调用自身的collect()方法来递归遍历指定的测试文件或目录,并返回一个列表items

        items = self.collect()

        # 调用hook函数pytest_collectreport表示收集测试用例结束,并生成收集报告

        self.hook.pytest_collectreport(report=CollectReport(self"passed", items))

        # 调用hook函数pytest_collection_modifyitems允许对收集到的Item对象进行修改

        self.hook.pytest_collection_modifyitems(session=self, config=self.config, items=items)

        # 调用hook函数pytest_deselected表示从收集到的Item对象中筛选出需要执行的Item对象

        self.hook.pytest_deselected(items=self.deselected)

        # 调用hook函数pytest_collection_finish表示收集和筛选测试用例完成,并返回最终要执行的Item对象列表

        self.hook.pytest_collection_finish(session=self)

        return items

     

    def runtestloop(self, items):

        # 调用hook函数pytest_runtestloop表示开始循环执行测试用例

        self.hook.pytest_runtestloop(session=self)

        # 遍历items列表,依次取出每个Item对象

        for item in items:

            # 调用Item对象的runtestprotocol()方法来执行单个测试用例的协议

            item.runtestprotocol()

        # 返回退出码0表示成功

        return 0

```

  

  Item对象是pytest的核心测试类,它负责封装和执行单个测试用例的信息,包括名称、位置、参数化信息、标记信息等。Item对象定义在src/_pytest/python.py中,它继承了Node类,也

就是说它可以作为一个测试节点。Item对象的runtestprotocol()方法是执行单个测试用例的主要入口,它会调用hook函数pytest_runtest_logstart来开始记录日志信息;然后调用runtest()方法

来执行测试用例的前置、主体和后置部分;最后调用hook函数pytest_runtest_logfinish来结束记录日志信息,并生成日志报告。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

```python

# src/_pytest/python.py

class Item(Node):

    def __init__(self, name, parent, config, session):

        # 初始化一些属性和状态信息

     

    def runtestprotocol(self):

        # 调用hook函数pytest_runtest_logstart表示开始记录日志信息

        self.ihook.pytest_runtest_logstart(nodeid=self.nodeid, location=self.location)

        # 调用runtest()方法来执行测试用例的前置、主体和后置部分,并返回一个列表reports

        reports = self.runtest()

        # 调用hook函数pytest_runtest_logfinish表示结束记录日志信息,并生成日志报告

        self.ihook.pytest_runtest_logfinish(nodeid=self.nodeid, location=self.location)

        return reports

     

    def runtest(self):

        # 创建一个空列表reports

        reports = []

        # 调用_setup()方法来执行测试用例的前置操作,并将返回的报告添加到reports列表中

        reports.append(self._setup())

        # 如果前置操作没有失败或跳过,则调用_call()方法来执行测试用例的主体部分,并将返回的报告添加到reports列表中

        if reports[-1].passed:

            reports.append(self._call())

        # 调用_teardown()方法来执行测试用例的后置操作,并将返回的报告添加到reports列表中

        reports.append(self._teardown())

        # 返回reports列表

        return reports

     

    def _setup(self):

        # 调用hook函数pytest_runtest_setup表示开始执行前置操作,并返回一个报告setup_report

        setup_report = self.ihook.pytest_runtest_setup(item=self)

        return setup_report

     

    def _call(self):

        # 调用hook函数pytest_runtest_call表示开始执行主体部分,并返回一个报告call_report

        call_report = self.ihook.pytest_runtest_call(item=self)

        return call_report

     

    def _teardown(self):

        # 调用hook函数pytest_runtest_teardown表示开始执行后置操作,并返回一个报告teardown_report

        teardown_report = self.ihook.pytest_runtest_teardown(item=self)

        return teardown_report

```

总结:

Pytest的加载流程大致如下:

- Pytest首先会解析命令行参数,确定要执行的测试文件、测试目录、测试类、测试函数等,以及一些配置选项。
- Pytest会根据配置文件(pytest.ini、setup.cfg、tox.ini等)和命令行参数,创建一个Config对象,用于存储配置信息。
- Pytest会创建一个Session对象,用于管理整个测试过程的上下文信息,包括收集测试用例、执行测试用例、生成测试报告等。
- Pytest会调用hook函数pytest_sessionstart,表示测试会话开始。
- Pytest会调用hook函数pytest_collectstart,表示开始收集测试用例。
- Pytest会根据Config对象中的信息,递归遍历指定的测试文件或目录,寻找符合pytest约定的测试用例(以test_开头的函数或方法,以Test开头的类等)。
- Pytest会将找到的测试用例封装成Item对象,并添加到Session对象的items列表中。Item对象包含了测试用例的名称、位置、参数化信息、标记信息等。
- Pytest会调用hook函数pytest_collectreport,表示收集测试用例结束,并生成收集报告。
- Pytest会调用hook函数pytest_collection_modifyitems,允许对收集到的Item对象进行修改,例如重新排序、添加或删除标记等。
- Pytest会调用hook函数pytest_deselected,表示从收集到的Item对象中筛选出需要执行的Item对象,并将不需要执行的Item对象放入Session对象的deselected列表中。
- Pytest会调用hook函数pytest_collection_finish,表示收集和筛选测试用例完成,并返回最终要执行的Item对象列表。
- Pytest会根据是否使用多进程或多线程模式,创建相应的WorkerController对象,用于管理多个Worker对象。Worker对象负责执行具体的测试用例,并将结果返回给WorkerController对象。
- Pytest会调用hook函数pytest_runtestloop,表示开始循环执行测试用例。
- Pytest会遍历Session对象中的items列表,依次取出每个Item对象,并调用hook函数pytest_runtest_protocol,表示开始执行单个测试用例的协议。
- Pytest会调用hook函数pytest_runtest_logstart,表示开始记录单个测试用例的日志信息。
- Pytest会调用hook函数pytest_runtest_setup,表示开始执行单个测试用例的前置操作(例如setup函数或方法)。
- Pytest会调用hook函数pytest_runtest_call,表示开始执行单个测试用例的主体部分(例如测试函数或方法)。
- Pytest会调用hook函数pytest_runtest_teardown,表示开始执行单个测试用例的后置操作(例如teardown函数或方法)。
- Pytest会调用hook函数pytest_runtest_logfinish,表示结束记录单个测试用例的日志信息,并生成日志报告。
- Pytest会调用hook函数pytest_runtest_makereport,表示根据单个测试用例的执行结果,生成测试报告(包括setup、call和teardown三个阶段的报告)。
- Pytest会重复上述步骤,直到所有的Item对象都被执行完毕。
- Pytest会调用hook函数pytest_sessionfinish,表示测试会话结束,并生成最终的测试报告(包括所有Item对象的报告)。
- Pytest会调用hook函数pytest_terminal_summary,表示在终端输出最终的测试结果和统计信息。

这可能是B站最详细的pytest自动化测试框架教程,整整100小时,全程实战!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/65794.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp项目实践总结(八)自定义加载组件

有时候一个页面请求接口需要加载很长时间,这时候就需要一个加载页面来告知用户内容正在请求加载中,下面就写一个简单的自定义加载组件。 目录 准备工作逻辑思路实战演练效果预览准备工作 在之前的全局组件目录components下新建一个组件文件夹,命名为q-loading,组件为q-loa…

Adobe Illustrator 2023 for mac安装教程,可用。

Adobe Illustrator 是行业标准的矢量图形应用程序,可以为印刷、网络、视频和移动设备创建logos、图标、绘图、排版和插图。数以百万计的设计师和艺术家使用Illustrator CC创作,从网页图标和产品包装到书籍插图和广告牌。此版本是2023版本,适配…

LeetCode(力扣)236. 二叉树的最近公共祖先Python

LeetCode236. 二叉树的最近公共祖先 题目链接代码 题目链接 https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/ 代码 # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val x # self.…

C语言深入理解指针(非常详细)(二)

目录 指针运算指针-整数指针-指针指针的关系运算 野指针野指针成因指针未初始化指针越界访问指针指向的空间释放 如何规避野指针指针初始化注意指针越界指针不使用时就用NULL避免返回局部变量的地址 assert断言指针的使用和传址调用传址调用例子(strlen函数的实现&a…

The Cherno——OpenGL

The Cherno——OpenGL 1. 欢迎来到OpenGL OpenGL是一种跨平台的图形接口(API),就是一大堆我们能够调用的函数去做一些与图像相关的事情。特殊的是,OpenGL允许我们访问GPU(Graphics Processing Unit 图像处理单元&…

pwngdb 中 b *$rebase(0x相对基址偏移) 是什么意思

pwngdb 中 b *$rebase(0x相对基址偏移) 是什么意思 pwngdb 是一个针对二进制漏洞利用的调试工具库,用于在 GDB 调试器中辅助进行漏洞开发和漏洞利用的调试。b *$rebase(0x相对基址偏移) 是 pwngdb 中的一个调试命令,用于在基地址重定位后设置断点。 在二…

Python小知识 - 如何使用Python的Flask框架快速开发Web应用

如何使用Python的Flask框架快速开发Web应用 现在越来越多的人把Python作为自己的第一语言来学习,Python的简洁易学的语法以及丰富的第三方库让人们越来越喜欢上了这门语言。本文将介绍如何使用Python的Flask框架快速开发Web应用。 Flask是一个使用Python编写的轻量级…

Spring Boot中通过maven进行多环境配置

上文 java Spring Boot将不同配置拆分入不同文件管理 中 我们说到了,多环境的多文件区分管理 说到多环境 其实不止我们 Spring Boot有 很多的东西都有 那么 这就有一个问题 如果 spring 和 maven 都配置了环境 而且他们配的不一样 那么 会用谁的呢? 此…

MySQL编写建表语句,如何优雅处理创建时间与更新时间

在 MySQL 中,可以使用 TIMESTAMP 或者 DATETIME 数据类型来存储日期和时间信息,并结合默认值和触发器来实现自动更新 createTime 和 updateTime 字段。 以下是一个示例建表语句,演示如何设置自动更新的 createTime 和 updateTime 字段&#…

《TCP/IP网络编程》阅读笔记--基于Windows实现Hello Word服务器端和客户端

目录 1--Hello Word服务器端 2--客户端 3--编译运行 3-1--编译服务器端 3-2--编译客户端 3-3--运行 1--Hello Word服务器端 // gcc hello_server_win.c -o hello_server_win -lwsock32 // hello_server_win 9190 #include <stdio.h> #include <stdlib.h> #i…

【算法刷题-双指针篇】

目录 1.leetcode-27. 移除元素2.leetcode-344. 反转字符串3.leetcode-剑指 Offer 05. 替换空格4.leetcode-206. 反转链表5.leetcode-19. 删除链表的倒数第 N 个结点6.leetcode-面试题 02.07. 链表相交7.leetcode-142. 环形链表 II8.leetcode-15. 三数之和9.leetcode-18. 四数之…

Git使用——GitHub项目回退版本

查看历史版本 使用git log命令查看项目的历史版本&#xff1a; 可以一直回车&#xff0c;直到找到想要的历史版本&#xff0c;复制commit后面的那一串id。 恢复历史版本 执行命令 git reset --hard 版本号&#xff1a; git reset --hard 39ac3ea2448e81ea992b7c4fdad9252983…

Ubuntu系统环境搭建(五)——Ubuntu安装maven

ubuntu环境搭建专栏&#x1f517;点击跳转 Ubuntu系统环境搭建&#xff08;五&#xff09;——Ubuntu安装maven 更新 sudo apt update安装 sudo apt install maven验证 mvn -version

ARM 汇编基础知识

1.为什么学习汇编&#xff1f; 我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编&#xff0c;因为 Cortex-A 芯片一 上电 SP 指针还没初始化&#xff0c; C 环境还没准备好&#xff0c;所以肯定不能运行 C 代码&#xff0c;必须先用汇编语言设置好 C 环境…

Python编程练习与解答 练习96:字符串是否表示整数

本练习将编写一个名为isInteger的函数&#xff0c;用于确定字符串中的字符是否代表有效整数&#xff0c;确定字符串是否表示整数时&#xff0c;则应忽略开通要或者结尾的任何空白。一旦这个空白被忽略&#xff0c;如果字符串的长度至少是1&#xff0c;且只包含数字&#xff0c;…

C++(16):模板与泛型编程

面向对象编程&#xff08;OOP&#xff09;和泛型编程都能处理在编写程序时不知道类型的情况。 不同之处在于&#xff1a;OOP 能处理类型在程序运行之前都未知的情况&#xff1b;而在泛型编程中&#xff0c;在编译时就能获知类型了。 模板是C中泛型编程的基础。一个模板就是一个…

七、Linux中一些符号的含义和宿主目录的介绍

1、Linux中一些符号的含义 在Linux命令行中&#xff0c;会看到如下一些符号&#xff0c;含义如下。 符号含义. 代表当前目录..代表上一层目录&#xff0c;当前目录的父目录-代表前一个目录&#xff0c;刚才从哪个目录cd过来~代表当前用户的宿主目录/代表根目录$普通用户的命…

两个线程同步执行:解决乱箭穿心(STL/Windows/Linux)

C自学精简教程 目录(必读) C并发编程入门 目录 多线程同步 线程之间同步是指线程等待其他线程执行完某个动作之后再执行&#xff08;本文情况&#xff09;。 线程同步还可以是像十字路口的红绿灯一样&#xff0c;只允许一个方向的车同行&#xff0c;其他方向的车等待。 本…

Mac 如何判断下载Mac with Intel Chip 还是 Mac with Apple Chip

如下图&#xff0c;当我们在 Mac系统 下载客户端时&#xff0c;有两种选择&#xff1a;Mac with Intel Chip 、 Mac with Apple Chip 如何判断要下载哪一种&#xff1f; 需要判断本机Mac是在Inter芯片还是Apple芯片上运行的。方法如下&#xff1a; 点击屏幕左上角Apple标志&a…

DHorse v1.3.2 发布,基于 k8s 的发布平台

版本说明 新增特性 构建版本、部署应用时的线程池可配置化&#xff1b; 优化特性 构建版本跳过单元测试&#xff1b; 解决问题 解决Vue应用详情页面报错的问题&#xff1b;解决Linux环境下脚本运行失败的问题&#xff1b;解决下载Maven安装文件失败的问题&#xff1b; 升…