一、指定目录或文件
pytest命令后面可以指定文件或者目录,当指定文件时,pytest会执行指定文件中的所有符合pytest规则的测试函数或测试方法,当指定目录时,则会递归的从指定目录中寻找符合条件的测试文件,然后执行测试文件中的所有测试函数或测试方法。下面以如下的目录结构介绍pytest命令如何指定文件或目录执行测试脚本。
tests
|--------demo01
|--------test_demo01.py
|--------test_demo02.py
|--------test_case03.py
|--------demo02
|--------test_demo04.py
|--------test_demo05.py
当使用pytest test_case03.py命令时,即指定测试文件,此时会执行test_case.03.py文件中的所有测试函数或测试方法,当使用pytest demo01命令时,即指定目录,此时会执行demo01目录下的所有测试文件中的测试函数和测试犯法,而当直接执行pytest命令时,此时相当于是指定了目录为当前目录,即此时会执行当前目录中以及所有子目录中的所有测试文件的测试函数或者测试方法。
二、指定测试函数或测试方法
pytest命令也可以直接指定具体的测试函数或测试方法以及测试类,指定测试函数时只执行指定的一个测试函数,如果指定类中的测试方法时同样也执行指定的一个测试方法,当指定测试类时,则执行指定类中的所有测试方法。下面以如下测试代码演示如何指定测试函数或测试方法,test_demo01.py代码如下:
def test_func1():
print("\nin test_demo01.test_func1......")
assert 1 == 1
def test_func2():
print("\nin test_demo01.test_func2......")
assert 1 == 1
class TestDemo(object):
def test_func1(self):
print("\nin test_demo01.TestDemo.test_func1......")
assert 1 == 1
def test_func2(self):
print("\nin test_demo01.TestDemo.test_func2......")
assert 1 == 1
如下指定测试函数时只执行指定的test_func1函数。
$ pytest -s test_demo01.py::test_func1
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests\demo01
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 1 item
test_demo01.py
in test_demo01.test_func1......
.
========================================================================== 1 passed in 0.02s ===========================================================================
$
如下指定测试类中的测试方法时只会执行指定的test_func1方法。
$ pytest -s test_demo01.py::TestDemo::test_func1
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests\demo01
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 1 item
test_demo01.py
in test_demo01.TestDemo.test_func1......
.
========================================================================== 1 passed in 0.01s ===========================================================================
$
如下当指定测试类时则会执行整个类中的所有测试方法。
$ pytest -s test_demo01.py::TestDemo
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests\demo01
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items
test_demo01.py
in test_demo01.TestDemo.test_func1......
.
in test_demo01.TestDemo.test_func2......
.
========================================================================== 2 passed in 0.02s ===========================================================================
$
三、通过-k参数对文件名类名函数名模糊匹配挑选用例执行
pytest -k 挑选脚本执行也是非常灵活的,pytest -k 后面跟的是一个字符串,然后从当前路径递归到子目录寻找测试脚本,当给定的字符串匹配的一个目录时,则此目录下所有用例均会被执行,当给定的字符串未匹配在目录名中匹配,则继续找目录中的测试文件,当测试文件匹配后,则此测试文件中的所有测试函数或测试方法均会被执行,而如果测试文件名未匹配时,则继续匹配测试文件中的测试类、测试函数、测试方法。
比如有目录结构如下:
demo01-----
|--------test_demo01.py
|--------test_demo02.py
其中test_demo01.py内容如下:
def test_func1():
print("\nin test_demo01.test_func1......")
assert 1 == 1
def test_func2():
print("\nin test_demo01.test_func2......")
assert 1 == 1
class TestDemo(object):
def test_func1(self):
print("\nin test_demo01.TestDemo.test_func1......")
assert 1 == 1
def test_func2(self):
print("\nin test_demo01.TestDemo.test_func2......")
assert 1 == 1
test_demo02.py内容如下:
def test_func1():
print("\nin test_demo02.test_func1......")
assert 1 == 1
def test_func2():
print("\nin test_demo02.test_func2......")
assert 1 == 1
当执行如下命令时,因为目录demo01中已经存在"1"了,因此此时demo01内的所有用例均会被选中执行。
$ pytest -s -k "1"
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests\demo01
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 6 items / 1 deselected / 5 selected
test_demo01.py
in test_demo01.test_func1......
.
in test_demo01.test_func2......
.
in test_demo01.TestDemo.test_func1......
.
in test_demo01.TestDemo.test_func2......
.
test_demo02.py
in test_demo02.test_func1......
.
=================================================================== 5 passed, 1 deselected in 0.02s ====================================================================
$
当执行如下命令时,显然demo02的目录名中没有2,因此继续找demo01中的文件,显然test_demo01.py文件名中也没有2,则此时继续进入test_demo01.py,测试函数test_func2显然命中,即会被执行,而TestDemo类型没有匹配,则继续在TestDemo类中找,此时TestDemo类中的test_func2测试方法命中,即在test_demo01.py文件中命中了一个测试函数和一个测试方法,而test_demo02.py文件名本身已经含有2了,因此test_demo02.py中的所有测试函数和测试方法均会被执行,执行结果如下,与上述分析一致。
$ pytest -s -k "2"
demo01\test_demo01.py
in test_demo01.test_func2......
.
in test_demo01.TestDemo.test_func2......
.
demo01\test_demo2.py
in test_demo02.test_func1......
.
in test_demo02.test_func2......
.
=================================================================== 4 passed, 2 deselected in 2.22s ====================================================================
$
此外,pytest -k 后面的表达式还支持逻辑关系匹配,如下命令的含义是匹配1但是不匹配func2,执行结果如下:
$ pytest -s -k "1 and not func2"
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: D:\src\blog\tests\demo01
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 6 items / 3 deselected / 3 selected
test_demo01.py
in test_demo01.test_func1......
.
in test_demo01.TestDemo.test_func1......
.
test_demo02.py
in test_demo02.test_func1......
.
=================================================================== 3 passed, 3 deselected in 0.02s ====================================================================
$
四、通过–ignore参数可以挑选用例忽略执行
–ignore参数相当于挑选出一部分用例不执行,比如–ignore给定的是目录,则此目录下的所有测试函数和测试方法均不会执行,如果–ignore后面给定的是测试文件,则此测试文件中的所有测试方法和测试函数均不会执行。
比如目录结构如下
tests/
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
使用pytest --ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/ ,可以使用多个ignore参数去忽略,执行结果如下,可以看到tests/foobar/test_foobar_03.py 和hello目录下的都没有执行
$ pytest --ignore=foobar/test_foobar_03.py --ignore=hello/
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 5 items
example\test_example_01.py . [ 20%]
example\test_example_02.py . [ 40%]
example\test_example_03.py . [ 60%]
foobar\test_foobar_01.py . [ 80%]
foobar\test_foobar_02.py . [100%]
========================================================================== 5 passed in 0.05s ===========================================================================
$
五、通过–ignore-glob参数支持正则挑选用例忽略
–ignore-glob参数的作用于–ignore的作用相同,都是将挑选出来的用例忽略不执行,不同的是–ignore参数需要指定的四目录或文件或测试函数,而–ignore-glob参数则可以通过正则的方式进行匹配,对匹配上的脚本均忽略不执行。
比如目录结构如下
tests/
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
在Unix shell风格下,使用–ignore-glob参数,可以通过正则匹配的方式忽略用例,如下,以“_01.py”结尾的用例均已被忽略
$ pytest --ignore-glob='*_01.py'
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 6 items
example\test_example_02.py . [ 16%]
example\test_example_03.py . [ 33%]
foobar\test_foobar_02.py . [ 50%]
foobar\test_foobar_03.py . [ 66%]
hello\workd\test_world_02.py . [ 83%]
hello\workd\test_world_03.py . [100%]
============================== 6 passed in 0.06s ==============================
六、通过–deselect参数挑选用例不执行并显示未执行数量
–deselect参数跟–ignore参数作用类似,都是挑选一部分用例不执行,不同的是,–ignore参数执行完成后只显示执行了多少条用例,而–deselect会显示有多少条用例被选中未执行,如下所示。
tests/
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
如下,使用pytest --deselect foobar/test_foobar_01.py --deselect foobar/test_foobar_03.py 命令,即可不执行test_foobar_01.py和test_foobar_03.py 文件的用例,如果忽略多个可以使用多个–deselect参数
$ pytest --deselect foobar/test_foobar_01.py --deselect foobar/test_foobar_03.py
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 9 items / 2 deselected / 7 selected
example\test_example_01.py . [ 14%]
example\test_example_02.py . [ 28%]
example\test_example_03.py . [ 42%]
foobar\test_foobar_02.py . [ 57%]
hello\workd\test_world_01.py . [ 71%]
hello\workd\test_world_02.py . [ 85%]
hello\workd\test_world_03.py . [100%]
======================= 7 passed, 2 deselected in 0.07s =======================
七、通过重复指定文件路径可实现用例重复执行
比如目录结构如下
tests/
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
当指定的重复路径为文件级别时,默认支持执行多次,如下,执行了两次
$ pytest foobar/test_foobar_01.py foobar/test_foobar_01.py
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items
foobar\test_foobar_01.py ..
============================== 2 passed in 0.02s ==============================
指定的重复路径为目录时,默认只会执行一次,如下,只执行了一次
$ pytest foobar foobar
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 3 items
foobar\test_foobar_01.py . [ 33%]
foobar\test_foobar_02.py . [ 66%]
foobar\test_foobar_03.py . [100%]
============================== 3 passed in 0.03s ==============================
当指定的路径为目录时,如果希望也执行多次,需要使用 --keep-duplicates参数,如下目录中的用例执行了2次
$ pytest foobar foobar --keep-duplicates
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 6 items
foobar\test_foobar_01.py . [ 16%]
foobar\test_foobar_02.py . [ 33%]
foobar\test_foobar_03.py . [ 50%]
foobar\test_foobar_01.py . [ 50%]
foobar\test_foobar_02.py . [ 50%]
foobar\test_foobar_03.py .
============================== 6 passed in 0.04s ==============================
八、通过–collect-only参数可以不执行脚本而统计脚本总数
有事需要查看有多少用例,但是又不想通过执行所有用例,此时可以使用–collect-only参数,比如目录结构如下
tests/
|-- pytest.ini
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
执行pytest --collect-only 查看当前目录下有多少用例
$ pytest --collect-only
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('G:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: G:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collecting ... collected 9 items
<Package example>
<Module test_example_01.py>
<Function test_example>
<Module test_example_02.py>
<Function test_example>
<Module test_example_03.py>
<Function test_example>
<Package foobar>
<Module test_foobar_01.py>
<Function test_example>
<Module test_foobar_02.py>
<Function test_example>
<Module test_foobar_03.py>
<Function test_example>
<Package workd>
<Module test_world_01.py>
<Function test_example>
<Module test_world_02.py>
<Function test_example>
<Module test_world_03.py>
<Function test_example>
========================= 9 tests collected in 0.22s ==========================
九、通过pytest.ini设置用例默认的搜索目录
pytest命令默认是从当前目录开始搜索,当然也是不建议去修改这个规则的,但是pytest是支持修改的,比如在一些特殊的情况下,需要修改默认的搜索目录,此时则可以通过pytest.ini很容易的进行设置。
同样比如如下目录:
tests/
|-- pytest.ini
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
假如在某些特殊的场景下,希望pytst只在foobar和hello目录下搜索脚本,而不能去其他目录下搜索,此时就可以通过pytest.ini中配置,如下
[pytest]
testpaths=foobar hello
执行结果如下,即指定了当执行pytest命令的时候只会去搜索foobar和hello目录
$ pytest
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests, configfile: pytest.ini, testpaths: foobar, hello
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 6 items
foobar\test_foobar_01.py . [ 16%]
foobar\test_foobar_02.py . [ 33%]
foobar\test_foobar_03.py . [ 50%]
hello\workd\test_world_01.py . [ 66%]
hello\workd\test_world_02.py . [ 83%]
hello\workd\test_world_03.py . [100%]
============================== 6 passed in 0.05s ==============================
在pytest.ini文件中可以通过norecursedirs指定目录不搜索
pytest.ini内容如下:
[pytest]
norecursedirs=foobar hello
执行结果如下,明显foobar和hello目录都没有搜索
$ pytest
============================= test session starts =============================
platform win32 -- Python 3.9.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: G:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, forked-1.3.0, rerunfaiures-10.1, xdist-2.3.0
collected 3 items
example\test_example_01.py . [ 33%]
example\test_example_02.py . [ 66%]
example\test_example_03.py . [100%]
============================== 3 passed in 0.04s ==============================
十、在IDE中通过右键执行当前文件的方式执行用例
若使用pycharm编辑器编写自动化脚本,有时候希望像执行代码一样执行自动化脚本,即在文件中右键即可执行,此时就需要在测试脚本里加几行代码,如下在编辑器中右键可以执行执行。
import pytest
def test_func1():
print("\nin test_case01.test_func1......")
assert 1 == 1
def test_func2():
print("\nin test_case01.test_func2......")
assert 1 == 1
if __name__ == "__main__":
pytest.main()
若指定pytest的参数,如下:
import pytest
def test_func1():
print("\nin test_case01.test_func1......")
assert 1 == 1
def test_func2():
print("\nin test_case01.test_func2......")
assert 1 == 1
if __name__ == "__main__":
pytest.main(['-s','test_case01.py'])