pytest探究:如何发现测试内容

pytest发现规则

  • 如果没指定参数,并且配置了 testpaths (详见1.testpaths),会从 testpaths 查找,否则从当前目录查找。也可以通过命令行指定目录,文件名,节点 id 的组合。
  • 递归目录,直到遇到 norecursedirs (详见3.norecursedirs)为止
  • 在所有目录中搜索 test_*.py 或者 *_test.py 文件,并通过测试包名(详见4. 测试包名)导入他们。
  • 从文件中收集测试项,包括:
    • test 开头的函数或类外方法
    • test 开头的函数或方法,在 Test 开头的类中(这个类不能有 init方法)

1.testpaths

从 rootdir 下的 testpaths 开始找 test(如果命令行没设置其他的目录,文件,或测试ids) ,它可以加速测试收集,过滤掉不想要的测试内容。

创建如下目录结构,在 tox.ini 文件中加入 testpaths :

image

tox.ini 配置文件如下:

[pytest]
testpaths = a/a1

可以看到, pytest 设置 tox.ini 文件为 inifile ,同时设置了 testpaths ,并且只从 a/a1 目录下收集测试项:

[
image

image1701×419 15.2 KB](https://ceshiren.com/uploads/default/original/2X/2/2e3c3ed384523af8f4cc67f8e510e1f0158b5090.png)

2.rootdir

rootdir 的作用如下:

  • 构建 nodeids时,每一个 test 都有唯一的标识 nodeid ,标识的根目录就是 rootdir ,包括绝对路径,类名, 函数名和参数化。
  • 存放插件的测试信息,比如 cache 1 插件在 rootdir 下创建了 .pytest_cache 目录存储交叉测试运行状态。

比如当 rootdir 为 pytest_example 时,其下就存在 cache 插件存放目录 .pytest_cache :

image

可以用命令行参数或者 ini_file 指定 `rootdir :

  • --rootdir=path 可以设置 rootdir 目录
  • pytest.ini 中的 addopts 参数可以修改 pytest 默认参数

pytest.ini 内容如下,它指定 pytest 命令的默认参数为 --rootdir=path ,当只执行 pytest 命令时,会自动加上 --rootdir=path

# content of pytest.ini or tox.ini
[pytest]
addopts = --rootdir=path

如果不显示指定 rootdir 的位置, pytest 会按照下述规则自动寻找 rootdir :

  • 寻找多个参数的共同祖先目录,如果没有,将当前工作目录设置为共同祖先目录。
  • 从共同祖先目录及上级目录中找三个文件 pytest.ini , tox.inisetup.cfg ,若找到其中任何一个文件,则将其变为 ini-file ,而它所在的目录就是 rootdir
  • 如果没有 ini-file ,从共同祖先目录及上级目录中找 setup.py ,如果有,则将其所在目录设为 rootdir
  • 如果没有 setup.py ,从每个参数及其上级目录中找 pytest.ini , tox.inisetup.cfg , 如果找到,它将成为 ini-file ,并且其目录将成为rootdir。
  • 如果没有 ini-file ,把共同祖先目录作为 rootdir,这么做的原因是,即使不是包,且没有配置文件,也能使用 pytest 。

比如以下目录,请注意其中的 pytest.ini 文件位置:

image

运行如下 pytest 命令, pytest 首先找到两个参数的公共祖先目录 a ,然后从 a 目录中找到了 pytest.ini 文件,于是这个文件就是 inifile ,而 rootdir 就是目录 a :

[
image

image1132×219 28.7 KB](https://ceshiren.com/uploads/default/original/2X/b/b3662a7dca7c51c8501690cf9bb8b9406abb0713.png)

将 pytest.ini 的位置放入 a1目录下:


image

pytest 首先找到了公共目录 a ,但是在目录 a 及其上级目录没有发现任何配置文件(pytest.ini , tox.ini 和 setup.cfg,setup.py),于是从每个参数及其上级目录中找,发现目录 a1 下存在 pytest.ini ,它将成为 ini-file ,并且其目录将成为rootdir。

[
image

image1166×217 27.9 KB](https://ceshiren.com/uploads/default/original/2X/8/8459740ccc0d34bfe8070cae9b2778f73f76580f.png)

没有 pytest.ini 文件时, rootdir 是 pytest_example 而不是 a , 比如下图

image

[
image

image1173×217 28.3 KB](https://ceshiren.com/uploads/default/original/2X/6/6ca3274a41472879e58ed16abc2cfcbc0d47f143.png)

如果没有参数, pytest 会把当前目录设为 rootdir 。比如参考以下目录结构和运行结果:

image

[
image

image1005×226 27.1 KB](https://ceshiren.com/uploads/default/original/2X/e/efd3d98414f2721188f69650bc5e61780e5a103a.png)

即使设置了 pytest.ini ,如果 pytest 没有参数的话,会把当前目录 a 设置为 rootdir :

image

[
image

image917×227 26.5 KB](https://ceshiren.com/uploads/default/original/2X/1/1ec2c9a6148d55a5d2f733b3f99e26f05e97cb86.png)

注意:只要存在 pytest.ini 文件,就能被 pytest 匹配上,但是 tox.inisetup.cfg 必须包含 [pytest] 或者 [tool:pytest] 部分才能被匹配,如果有多个 ini-file ,最终只会挑选出一个(优先选择pytest.ini)。 pytest 存在一个 bug ,用户可以自定义插件,插件可以传参,如果传入的是路径,比如 pytest --log-output ../../test.log args,上述插件不写 args 的话, pytest 会使用 test.log 文件夹确定rootdir(参见issue 1435),比如 pytest --log-output ../../test.log 。即使用点 . 来引用当前工作目录,也会发生上述问题。

3.配置 norecursedirs

https://docs.pytest.org/en/latest/reference.html#confval-norecursedirs 1

设置目录的基本名字表达式(fnmatch-match风格),用于是否进行递归。

*            匹配所有内容
?           匹配所有单字符
[seq]     匹配 seq 中的任一个字符
[!seq]    匹配不在 seq 中的字符

默认表达式有 :

'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv' ,可以设置norecursedirs替代默认表达式,比如不递归指定目录:

[pytest] 
norecursedirs = .svn _build tmp*

pytest 会通过 activation 脚本识别并忽略 python 虚拟环境, pytest 不会收集虚拟环境中的 test ,但可以使用 ‑‑collect‑in‑virtualenv 选项开启收集。注意,norecursedirs 级别高于 ‑‑collect‑in‑virtualenv ,比如你开启了 ‑‑collect‑in‑virtualenv 选项,就必须重写 norecursedirs 才能让其生效(因为默认值有 venv 会过滤掉虚拟环境)。

比如下面的目录结构,在 pytest_example 目录下存在配置文件 tox.ini :

image

tox.ini 的内容如下:

[pytest]
norecursedirs = a1

[
image

image1510×406 14.2 KB](https://ceshiren.com/uploads/default/original/2X/5/5f8afaa0b020e9010b4a4147d9a2d99f41e6220a.png)

4. 测试包名

如果 pytest 发现一个测试文件 a/b/test_module.py ,会发生以下情况:

  • 设置 basedir :发现第一个不包含 __init__.py 的上级目录,如果 ab 都包含 __init__.py 文件, a 的父目录将成为 basedir
  • 使用 sys.path.insert(0, basedir) 将 basedir 置于环境变量的首位。
  • import a.b.test_module 把路径分割符 \ 转换成了 “.” ,让目录名和文件名直接映射到导入名。

在较大的项目中,可能会相互导入多个测试模块,规范的导入名称可规避意外情况,例如一个测试模块被导入两次。

测试

1. 无参数
现在不对 pytest 设置额外参数,不设置 testpaths ,也不设置 norecursedirs 。假设以下目录结构,在目录 pytest_example 下有一个包 a ,它下面 存在一个 test_a.py 和目录 a1 ,目录 a1 下面存在 test_a2.py:

pytest_example/
|- a/
   |- __init__.py
   |- test_a.py
   |- a1/
      |- tests/
         |- test_a1.py

test_a.py 和 test_a1.py 的代码和执行结果分别如下:


# 以下是 test_a.py 代码
def test_a():
    print('test_a')
# 以下是 test_a2.py 代码
class Test_A1:
    def test_a1(self):
        print("test_a1")

使用命令行 cd 到 pytest_example 目录下,执行 pytest 命令,其结果如下:

[
image

image995×273 30.2 KB](https://ceshiren.com/uploads/default/original/2X/4/42e0d5faa63967a83976d8e8e9d1a8334ccc5aa7.png)

可以看出, pytest 从 pytest_example 目录开始递归收集测试,收集到了两个测试内容: test_a.py 和 test_a1.py,它不会区分包和目录,也不会捕获标准输出, 输出结果打印 rootdir 的内容 ,后面会讲它的作用。

更多技术文章可点击获取 http://qrcode.testing-studio.com/f?from=jianshu&url=https://ceshiren.com/t/topic/3822

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容