【原文链接】Pytest自动化脚本默认的命名规则以及如何定制自定义的命名规则
一、测试脚本文件名默认地命名规则
测试脚本文件为python文件,此外文件名命名规则为test_.py或者_test.py格式的文件,如下列均为符合pytest要求的测试文件命名规范:
test_demo.py
test_01.py
test_.py
demo_test.py
01_test.py
_test.py
如下则均为不符合pytest要求的文件命名规则,即如果测试文件命名类似如下,则pytest会找不到测试脚本:
test.py
testdemo.py
Test_demo.py
TestDemo.py
Test_.py
Demo_Test.py
_Test.py
Test.py
Demo.py
这里面有一点需要格外注意的是,由于windows上文件名是不区分大小写的,因为默认情况下windows会把所有字符转换为小写处理,因此如果在windows上调试,可能会发现类似Test_demo.py文件pytest是能发现用例的,注意这种情况下是windows的特殊处理导致的,因此这里一定不要混淆,否则待后续将脚本迁移到linux服务器上执行的时候就会出现比如windows上能执行在linux上不执行的诡异事件。因此这里统一按照标准来,即测试文件必须为test_.py或者_test.py格式。
二、测试函数测试类名默认地命名规则
在测试脚本中,测试函数又分为两类,一种是直接定义在测试文件中的,比如如下:
def test_func():
assert 1==1
另一种则是使用类组织的在类内的测试函数,比如如下:
class TestDemo:
def test_func():
assert 1==1
在这里为了方便记忆,不论类外还是类内统一称为测试函数,如此便可以将测试类和测试函数命名规则总结为如下规则:
- 测试函数名必须以test开头
- 测试类名必须以Test开头
- 测试类中不能有__init__(self)方法
比如如下的测试函数均为符合pytest规则的测试函数
def test_demo():
assert 1==1
def testdemo():
assert 1==1
def test():
assert 1==1
def test_():
assert 1==1
而如下测试函数则均为不符合pytest规则的函数,即不会被pytest发现。
def demo_test():
assert 1==1
def demotest():
assert 1==1
def _test():
assert 1==1
def Test():
assert 1==1
def Test_Demo():
assert 1==1
def TestDemo():
assert 1==1
def DemoTest():
assert 1==1
而对于使用类组织的测试函数,首先类必须满足要求,即类型以Test开头,并且类中没有__init__方法,然后类中的测试函数名再符合测试函数的命名规则即以test开头时,才会被认为是测试脚本,如下:
class TestDemo:
def test_demo(self):
assert 1==1
class Test:
def test_demo(self):
assert 1==1
如果类名不是以Test开头或者类名以Test开头但是类中有__init__方法时,不论类中的测试函数名是否符合pytest要求的规则,均不会被pytest识别。
下面以一个综合的例子来演示一下,代码如下,这里为了演示具体哪个测试函数能被pytest识别到,在每个测试函数中加了打印信息,如下:
def demo_test():
print("in demo_test")
assert 1==1
def test_demo():
print("in test_demo")
assert 1==1
class TestDemo:
def __init__(self):
pass
def test_demo(self):
print("in TestDemo.test_demo")
assert 1==1
class Test:
def test_demo(self):
print("in Test.test_demo")
assert 1==1
然后使用 pytest -s命令,执行结果如下,加 -s 参数时为了将打印信息打印出来:
$ pytest -s
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0
rootdir: D:\redrose2100-book\ebooks\Pytest企业级应用实战\src
collected 2 items
test_demo.py in test_demo
.in Test.test_demo
.
=========================================================================== warnings summary ===========================================================================
test_demo.py:10
D:\redrose2100-book\ebooks\Pytest企业级应用实战\src\test_demo.py:10: PytestCollectionWarning: cannot collect test class 'TestDemo' because it has a __init__ constructo
r (from: test_demo.py)
class TestDemo:
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
===================================================================== 2 passed, 1 warning in 0.02s =====================================================================
可以kan看出,这里打印出了“in test_demo”和“in Test.test_demo”,只有这两个测试函数被pytest识别到了,此外还打印出了一条告警信息“cannot collect test class ‘TestDemo’ because it has a init constructo
r”,即提示TestDemo类中因为有__init__构造函数因而无法识别其中的测试函数。
三、测试脚本目录的约束条件
pytest对测试脚本目录的文件夹命名没有什么要求,但是有如下两条约定:
- 如果每一个文件夹中均创建有__init__.py文件,则没有其他要求
- 如果每一个文件夹中没有创建__init__.py文件,此时要去所有的测试脚本文件名必须独一无二,否则pytest会报错。
首先按照如下结构创建文件及文件夹目录
base
|--------demo01
|--------__init__.py
|--------test_demo.py
|--------demo02
|--------__init__.py
|--------test_demo.py
并zai在每个test_demo.py文件中编写一条最简单的测试脚本,比如:
def test_demo():
assert 1==1
执行pytest命令,结果如下:
$ pytest
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0
rootdir: D:\redrose2100-book\ebooks\Pytest企业级应用实战\src
collected 2 items
demo01\test_demo.py . [ 50%]
demo02\test_demo.py . [100%]
========================================================================== 2 passed in 0.11s ===========================================================================
此时,虽然两个测试文件的文件名相同,因为demo01和demo02文件夹中均有__init__.py文件,因此此时执行pytest是不会报错的,即当文件夹中均有__init__.py文件时,没有其他要求或约束。
下面将demo01和demo02文件夹中的__init__.py文件均删除,即目录结构如下:
base
|--------demo01
|--------test_demo.py
|--------demo02
|--------test_demo.py
此时执行pytest命令,结果如下:
$ pytest
_________________________________________________________________ ERROR collecting demo02/test_demo.py _________________________________________________________________
import file mismatch:
imported module 'test_demo' has this __file__ attribute:
D:\redrose2100-book\ebooks\Pytest企业级应用实战\src\demo01\test_demo.py
which is not the same as the test file we want to collect:
D:\redrose2100-book\ebooks\Pytest企业级应用实战\src\demo02\test_demo.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
======================================================================= short test summary info ========================================================================
ERROR demo02/test_demo.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=========================================================================== 1 error in 0.10s ===========================================================================
可以发现此时报错了,提示文件名要用独一无二的名称。
这里是跟pytest加载用例的原理有关,pytest在加载用例的时候实质是把测试用例文件当做模块导入的,而在导入模块之前,pytest首先是把一个basedir的变量添加到环境变量中,basedir的计算原理是针对每个测试脚本,向上递归的查找,找到最顶层带有__init__.py文件的目录,然后这个目录的上层目录作为basedir添加到环境变量中,然后根据basedir计算每个测试脚本文件的相对路径,这么说比较枯燥,以下面的目录结构为例:
针对第一个test_demo.py文件,从当前目录开上往上找,找到最后一层带有__init__.py文件的目录,此时demo01本身就没有__init__.py文件,因此pytest会把demo01的目录添加到环境变量中,然后从demo01开始确定第一个test_demo.py的导入的相对路径,即test_demo.py
针对第二个test_demo.py文件,同样从当前目录开始往上找,找到最后一层带有__init__.py文件的目录,这里同样demo02本身也没有__init__.py文件,此时pytest会把这个目录demo02添加到环境变量中,然后从demo02开始计算第二个test_demo.py的导入的相对路径,即test_demo.py
即此时发现这两个相同名字的文件虽然在不同层次不同文件夹中,但是经过pytest计算发现他们两个的导入路径完全相同,因此此时就会报错
至于pytest为什么会这么去计算,将在后续pytest脚本加载原理中作为参考详细的讲解,这里暂时不再深入探讨了。
四、自定义测试文件名、测试类、测试函数命名规则
通过pytest.ini可以自定义测试文件、测试类、测试函数的命名规则,一般情况下不建议去修改测试文件名、测试类、测试函数的默认的命名规则,因为pytest定义的默认的规则,相当于定义好了协议一样,大家都是认可和熟知的,假如重新定义了命名规则,则必然带来一个问题时其他人不了解不清楚当前的规则是什么样的,这样就带来了许多不必要的麻烦。
但是在有一些稍大一些的公司,他们可能会考虑根据公司业务的特点,制定公司级的标准,此时也是可以对默认的命名规则进行重新定义的。定义的方法也很简单,比如某公司觉得测试文件名和测试函数名的规则不一致很容易导致混乱,因此想根据自己的需求统一一下,即约束测试文件命名规则为check_*.py格式,测试类以Check开头,测试函数为check_*格式,这样就做到了形式上的统一,此时只需要在脚本的根目录下的pytest.ini文件中增加如下配置即可:
[pytest]
python_files = check_*.py
python_classes = Check
python_functions = check_*
下面就以一个实例演示,文件目录结构如下:
base
|----pytest.ini
|----check_demo.py
其中pytest.ini的配置代码为:
[pytest]
python_files = check_*.py
python_classes = Check
python_functions = check_*
check_demo.py中的测试函数和测试类的代码如下:
def check_func():
assert 1==1
class CheckDemo():
def check_func2(self):
assert 1==1
执行结果如下,可以看出此时测试文件已经不是默认命名规则了,测试类和测试函数的命名均已不是pytest默认的命名规则,而是自定义的规则,而pytest此时依然可以识别到并且执行,这就是pytest.ini配置的原因。
$ pytest
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0
rootdir: G:\redrose2100_book\ebooks\Pytest企业级应用实战\src, configfile: pytest.ini
collected 2 items
check_demo.py .. [100%]
========================================================================== 2 passed in 0.01s ===========================================================================