Pytest--使用与调用

1. 通过python -m pytest调用pytest

你可以通过python的解释器来执行测试:

python -m pytest

这和直接执行pytest [...]命令的效果几乎是一样的。

2. pytest执行结束时返回的状态码

运行pytest可能导致六种不同的退出代码:

  • ExitCode0:所有收集到的测试用例通过
  • ExitCode1:测试已收集并运行,但有些测试失败
  • ExitCode2:测试执行被用户中断
  • ExitCode3:执行测试时发生内部错误
  • ExitCode4:pytest命令使用错误
  • ExitCode5:未收集测试用例

ExitCode可以通过以下方式直接导入和访问:

from pytest import ExitCode
3. 获取版本信息
pytest --version  # 查看版本号和pytest的引入路径
4. 获取内置函数参数
pytest --fixtures
6. 获取帮助信息
pytest -h | --help  # 查看帮助信息
6. 在第一次(或n次)故障后停止

pytest可以设置在第一(n)次失败后停止测试过程:

pytest -x					#第一次测试失败后停止测试

pytest --maxfail=2			#第二次测试失败后停止测试
7. 指定/选择测试用例

在模块中运行测试

pytest test_mod.py

在目录中运行测试

pytest testing/

按关键字表达式运行测试

pytest -k "MyClass and not method"
# 示例
class TestMyClass:
    def test_something(self):
        assert 0
    
    def test_method_simple(self):
        assert True
> pytest -k "MyClass and not method" TestMyClass.py
================================================= test session starts =================================================
platform win32 -- Python 3.9.9, pytest-7.0.1, pluggy-1.0.0
rootdir: D:\Code\Pytest
plugins: allure-pytest-2.9.45, html-3.1.1, metadata-1.11.0
collected 2 items / 1 deselected / 1 selected

TestMyClass.py F                                                                                                 [100%]

====================================================== FAILURES =======================================================
_____________________________________________ TestMyClass.test_something ______________________________________________

self = <TestMyClass.TestMyClass object at 0x000002803C5C9FD0>

    def test_something(self):
>       assert 0
E       assert 0

TestMyClass.py:3: AssertionError
=============================================== short test summary info ===============================================
FAILED TestMyClass.py::TestMyClass::test_something - assert 0
=========================================== 1 failed, 1 deselected in 0.09s ===========================================

这将运行包含与给定名称匹配的名称的测试字符串表达式(不区分大小写),它可以包括使用文件名、类名和函数名作为变量的Python运算符。上面的例子将运行TestClass.test_something而不是TestClass.test_method_simple

8. 按节点ID运行测试

pytest为每一个收集到的测试用例指定一个唯一的nodeid,它由模块名加说明符构成,中间用::间隔。其中,说明符可以是类名、函数名以及由parametrize标记赋予的参数

# test_nodeid.py
import pytest

def test_one():
    print("test_one")
    assert 1

class TestNodeId:
    def test_two(self):
        print("TestNodeId::test_two")
        assert 1

    @pytest.mark.parametrize('x,y',[(1,1),(3,4)])
    def test_three(self,x,y):
        print(f"TestNodeId::test_three::{x} == {y}")
        assert x == y

上述实例中,我们创建了三个测试用例,分别对应不同的说明符:

  • 指定函数名执行

    >pytest -q -s test_nodeid.py::test_one
    2022-03-12 18:48:31
    test_one
    .
    1 passed in 0.01s
    
  • 指定类名+函数名执行

    > pytest -q -s test_nodeid.py::TestNodeId::test_two
    2022-03-12 18:50:27
    TestNodeId::test_two
    .
    1 passed in 0.00s
    
  • 指定由parametrize标记赋予的参数执行

    > pytest -q -s test_nodeid.py::TestNodeId::test_three[1-1]
    2022-03-12 18:54:50
    TestNodeId::test_three::1 == 1
    .
    1 passed in 0.00s
    

​ 这里对参数xy赋值的形式是[1-1],中间以-间隔;

​ 单个或多个参数的赋值形式类比,并且只能为[1-1]或者[3-4],其他的会报错。

注意:

这里也可以使用-k选项达到同样的效果:

  • 首先,可以使用--collect-only选项查看用例名:
> pytest -q -s --collect-only test_nodeid.py
2022-03-12 19:02:12
test_nodeid.py::test_one
test_nodeid.py::TestNodeId::test_two
test_nodeid.py::TestNodeId::test_three[1-1]
test_nodeid.py::TestNodeId::test_three[3-4]
  • 然后,使用-k执行符合规则的用例,例如:执行test_nodeid.py::test_one
> pytest -q -s -k 'test_one and not TestNodeId' ./test_nodeid.py
2022-03-12 19:04:29
test_one
.
1 passed, 3 deselected in 0.00s

结果和执行pytest -q -s test_nodeid.py::test_one效果一样。

8.1 执行指定标记的用例
pytest -m slow

这种方式会运行所有通过装饰器@pytest.mark.slow进行装饰的测试用例。

8.2 执行指定包中的测试用例
pytest --pyargs pkg.testing

这种方式会导入pkg.testing,并且基于该包所在位置来查找并运行测试用例

9. 修改Python回溯打印
pytest -l, --showlocals # 打印本地变量

pytest --tb=auto    # 默认模式
pytest --tb=long    # 尽可能详细输出
pytest --tb=short   # 更简短的输出
pytest --tb=line    # 每个失败信息总结在一行中
pytest --tb=native  # Python 的标准输出
pytest --tb=no      # 不打印失败信息

--full-trace是一种比--tb=long更详细的输出模式。甚至能观察到用户打断执行(Ctrl + C)时的回溯信息,而上述六种模式默认是不输出此类信息的。

10. 详细总结报告

使用-r选项可以在执行结束后,打印一个简短的总结报告。在执行测试用例很多时,可以让你对结果有个清晰的了解(包含所有故障、跳过、xfails等)

# test_report.py
import pytest
@pytest.fixture
def error_fixture():
    assert 0

def test_ok():
    print("ok")

def test_fail():
    assert 0

def test_error(error_fixture):
    pass

def test_skip():
    pytest.xfail("xfailing this test")

@pytest.mark.xfail(reason="always xfail")
def test_xpass():
    passs
> pytest -q -rA .\test_report.py
.FExx                                                                                                            [100%]
======================================================= ERRORS ========================================================
____________________________________________ ERROR at setup of test_error _____________________________________________

    @pytest.fixture
    def error_fixture():
>       assert 0
E       assert 0

test_report.py:4: AssertionError
====================================================== FAILURES =======================================================
______________________________________________________ test_fail ______________________________________________________

    def test_fail():
>       assert 0
E       assert 0

test_report.py:10: AssertionError
======================================================= PASSES ========================================================
_______________________________________________________ test_ok _______________________________________________________
------------------------------------------------ Captured stdout call -------------------------------------------------
ok
=============================================== short test summary info ===============================================
PASSED test_report.py::test_ok
XFAIL test_report.py::test_skip
  reason: xfailing this test
XFAIL test_report.py::test_xpass
  always xfail
ERROR test_report.py::test_error - assert 0
FAILED test_report.py::test_fail - assert 0
1 failed, 1 passed, 2 xfailed, 1 error in 0.11s

-r选项后面要紧接这一个参数,用于过滤显示测试用例的结果。

以下是所有有效的字符参数:

  • f:失败的
  • E:出错的
  • s:跳过执行的
  • x:跳过执行,并标记为xfailed的
  • X:跳过执行,并标记为xpassed的
  • p:测试通过的
  • P:测试通过,并且有输出信息的;即用例中有print
  • a:除了测试通过的,其他所有的;即除了pP
  • A:所有的

上述字符参数可以叠加使用,例如:我们期望过滤出失败的和未执行的:

pytest -rfs
11. 失败时加载PDB(Python Debugger)环境

PDBPython内建的诊断器,pytest允许通过以下命令在执行失败时进入这个诊断器模式:

pytest --pdb

pytest会在测试用例失败(或者Ctrl + C)时,调用这个诊断器:

# \Code\Pytest\test_pdb.py
def test_fail():
    x = 1
    assert x == 0
> pytest -q --pdb .\test_pdb.py
F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_fail():
        x = 1
>       assert x == 0
E       assert 1 == 0

test_pdb.py:3: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB post_mortem (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> d:\code\pytest\test_pdb.py(3)test_fail()
-> assert x == 0
(Pdb) x
1
(Pdb) import sys
(Pdb) sys.last_value
AssertionError('assert 1 == 0')
(Pdb) sys.last_type
<class 'AssertionError'>
(Pdb) sys.last_traceback
<traceback object at 0x000001CDBB27E900>
(Pdb)

你还可以访问测试用例的本地变量x

失败信息存储在sys.value,sys.last_type,sys.last_traceback变量中,你可以在交互环境中访问它们;

使用exit命令,退出PDB环境。

12. 开始执行时就加载PDB环境

通过以下命令,pytest允许你在每个测试用例开始执行时,就加载PDB环境:

pytest --trace
13. 设置断点

在测试用例代码中添加import pdb;pdb.set_trace(),当其被调用时,pytest会停止这条用例的输出:

  • 其他用例不受影响
  • 通过continue命令,退出PDB环境,并继续执行用例;
14. 使用内置的中断函数

Python有一个内置breakpoint()函数。pytest可以在以下场景中支持使用:

  • breakpoint被调用,并且PYTHONBREAKPOINTNone时,pytest会使用内部自定义的PDB代替系统的;
  • 测试执行结束时,自动切回系统自带的PDB
  • 当加上--pdb选项时,breakpoint和测试发生错误时,都会调用内部自定义的PDB
  • --pdbcls选项允许指定一个用户自定义的PDB
15. 分析测试执行时间

获取执行最慢的10个测试用例:

pytest --durations=10

默认情况下,pytest不会显示执行时间<0.01s的测试用例,可以使用-vv选项查看它们

16. 错误句柄

在测试执行中发生段错误或者超时的情况下,faulthandler标准模块可以转储python的回溯信息;它在pytest的执行中默认使能,使用-p no:faulthandler选项可以关闭它;同样,faulthandler_timeout=x配置项,可用于当测试用例的完成时间超过x秒时,转储所有线程的python回溯信息:

# pytest.ini
[pytest]
faulthandler_timeout=5

配置测试执行的超时时间是5秒;

import time
def test_faulthandler():
    time.sleep(7)
    assert 1

测试用例中添加等待7秒的操作;

  • 默认使能faulthandler的情况:

    > pytest -q .\test_fault_handler.py
    Timeout (0:00:05)!
    Thread 0x000024c8 (most recent call first):
      File "D:\Code\Pytest\test_fault_handler.py", line 3 in test_faulthandler
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\python.py", line 192 in pytest_pyfunc_call
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\python.py", line 1718 in runtest
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 168 in pytest_runtest_call
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 261 in <lambda>
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 340 in from_call
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 260 in call_runtest_hook
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 221 in call_and_report
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 132 in runtestprotocol
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 113 in pytest_runtest_protocol
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 347 in pytest_runtestloop
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 322 in _main
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 268 in wrap_session
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 315 in pytest_cmdline_main
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec
      File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py", line 165 in main
      File "D:\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py", line 188 in console_main
      File "D:\Software\Python\Python39\Scripts\pytest.exe\__main__.py", line 7 in <module>
      File "D:\Software\Python\Python39\lib\runpy.py", line 87 in _run_code
      File "D:\Software\Python\Python39\lib\runpy.py", line 197 in _run_module_as_main
    .                                                        [100%]
    1 passed in 7.02s
    

    在执行刚超过5秒的时候打印出回溯信息。但不会中断测试的执行;

  • 去使能faulthandler的情况:

    > pytest -q -p no:faulthandler .\test_fault_handler.py
    .                                                        [100%]
    ====================== warnings summary =======================
    ..\..\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py:1249
      D:\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py:1249: PytestConfigWarning: Unknown config option: faulthandler_timeout
    
        self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
    
    -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
    1 passed, 1 warning in 7.02s
    

    超时并不会触发回溯信息的打印。

注意:

这个功能是从pytest-faulthandler插件合并而来的,但是有两点不同:

  • 去使能时,使用-p no:faulthandler代替原来的--no-faulthandler
  • 使用faulthandler_timeout配置项代替--faulthandler-timeout命令行选项来配置超时时间。当然,你也可以使用-o faulthandler_timeout=x在命令行配置;
17. 创建JunitXML格式的测试报告

使用如下命令,可以在指定的path中创建一个能被Jenkins或者其他CI工具读取的XML格式的测试报告:

pytest --junitxml=path

你可以在项目的pytest.ini文件中,通过设置junit_suite_name的值,自定义XML文件中testsuite根节点的name信息:

[pytest]
junit_suite_name = pytest_chinese_doc

执行一个测试用例test_nodeid.py::test_one看看效果:

> pytest -q --junitxml=.\report\test_one.xml test_nodeid.py::test_one

生成的XML测试报告:

<?xml version="1.0" encoding="utf-8"?>
<testsuites>
    <testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.007" timestamp="2022-03-14T10:16:40.235646" hostname="LAPTOP-LEQLNICU">
        <testcase classname="test_nodeid" name="test_one" time="0.000" />
    </testsuite>
</testsuites>

我们可以看到,<testsuite>节点的name属性的值,变为我们所期望的pytest_chinese_doc,而不是默认的pytest;Junit XML规定time属性应该表明测试用例执行的全部耗时,包含setupteardown中的操作,这也是pytest的默认行为;如果你只想记录测试用例执行的时间,只需要作如下配置:

# pytest.ini
junit_duration_report = call
18. 在报告中为测试用例附加额外的子节点信息

我们有两种方式实现这个功能:

  • 使用record_property fixture

    test_record_property用例添加一个额外的testid

    # test_xml_report.py
    def test_record_property(record_property):
        record_property("test_id",10010)
        assert 1
    

    在报告中表现为<property name="test_id" value="10010" />

    <?xml version="1.0" encoding="utf-8"?>
    <testsuites>
    	<testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.018" timestamp="2022-03-14T11:20:12.295733" hostname="LAPTOP-LEQLNICU">
    		<testcase classname="test_xml_report" name="test_record_property" time="0.001">
    			<properties>
    				<property name="test_id" value="10010" />
    			</properties>
    		</testcase>
    	</testsuite>
    </testsuites>
    
  • 解析一个自定义的标记@pytest.mark.test_id()

    首先,修改pytest_collection_modifyitems钩子方法,添加对test_id标记的支持:

    # conftest.py
    def pytest_collection_modifyitems(session,config,items):
        for item in items:
            for maker in item.iter_makers(name="test_id"):
                test_id = maker.args[0]
                item.user_properties.append("test_id",test_id)
    

    然后,修改测试用例:

    import pytest
    @pytest.mark.test_id(10010)
    def test_record_property1():
        assert 1
    

    执行测试用例

    > pytest -q --junitxml=.\report\test_record_property1.xml test_xml_report.py
    .                                                                                                                                                    [100%]
    ==================================================================== warnings summary =====================================================================
    test_xml_report.py:8
      D:\Code\Pytest\test_xml_report.py:8: PytestUnknownMarkWarning: Unknown pytest.mark.test_id - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
        @pytest.mark.test_id(10010)
    
    -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
    ------------------------------------------- generated xml file: D:\Code\Pytest\report\test_record_property1.xml -------------------------------------------
    1 passed, 1 warning in 0.01s
    

    在报告中的也表现为<property name="test_id" value="10010" />

    <?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.016" timestamp="2022-03-15T09:29:04.823852" hostname="LAPTOP-LEQLNICU">
            <testcase classname="test_xml_report" name="test_record_property1" time="0.000">
                <properties>
                    <property name="test_id" value="10010" />
                </properties>
            </testcase>
        </testsuite>
    </testsuites>
    

    注意:这里我们会收到一个警告:

    PytestUnknownMarkWarning: Unknown pytest.mark.test_id - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.test_id(10010)

    – Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html

    这是因为我们没有在pytest中注册test_id标记,但不影响正常的执行;

    如果你想去除这个警告,只需要在pytest.ini的配置文件中注册这个标记:

    [pytest]
    markers=
    test_id:为测试用例添加ID
    

注意:

变动后的报告可能不符合最新的JUnitXML的模式检查规则,导致在某些CI工具上可能会发生未知的错误。

19. 在报告中为测试用例附加额外的属性信息

可以通过record_xml_attribute fixture为测试用例附加额外的属性,而不像record_property为其添加子节点;

为测试用例添加一个test_id属性,并修改原先的classname属性:

# test_xml_report.py
def test_record_property2(record_property):
    record_xml_attibute('test_id',10010)
    record_xml_attribute('classname','custom_classname')
    assert 1

执行测试用例

> pytest -q --junitxml=.\report\test_record_property2.xml test_xml_report.py
.                                                                                                                                                    [100%]
==================================================================== warnings summary =====================================================================
test_xml_report.py::test_record_property2
  test_xml_report.py:13: PytestExperimentalApiWarning: record_xml_attribute is an experimental feature
    def test_record_property2(record_xml_attribute):

test_xml_report.py::test_record_property2
  test_xml_report.py:13: PytestWarning: record_xml_attribute is incompatible with junit_family 'xunit2' (use 'legacy' or 'xunit1')
    def test_record_property2(record_xml_attribute):

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
------------------------------------------- generated xml file: D:\Code\Pytest\report\test_record_property2.xml -------------------------------------------
1 passed, 2 warnings in 0.02s

在报告中的表现为<testcase classname="custom_classname" name="test_record_property2" time="0.005" />

<?xml version="1.0" encoding="utf-8"?>
<testsuites>
    <testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.020" timestamp="2022-03-15T11:10:35.459391" hostname="LAPTOP-LEQLNICU">
        <testcase classname="custom_classname" name="test_record_property2" time="0.005" />
    </testsuite>
</testsuites>

注意:

  • record_xml_attribute目前是一个实验性的功能,未来可能被更强大的APU所替代,但功能本身会被保留。
  • 变动后的报告可能不符合最新的JUnitXML的模式检查规则,导致在某些CI工具上可能会发生未知的错误。
20. 在报告中为测试集附加额外的子节点信息

可以通过自定义一个session作用域级别的fixture,为测试集添加子节点信息,并且会作用于所有的测试用例;

这个自定义的fixture需要调用另外一个record_testsuite_property fixture

record_testsuite_property接收两个参数namevalue以构成<property>标签,其中,name必须以字符串,value会转换为字符串并进行XML转义:

# test_xml_report.py

@pytest.fixture(scope="session")
def log_global_env_facts(record_testsuite_property):
    record_testsuite_property("EXECUTOR","luizyao")
    record_testsuite_property("LOCATION","NJ")


def test_record_property3(log_global_env_facts):
    asserrt 1

执行测试用例:

> pytest -q --junitxml=.\report\test_record_property3.xml test_xml_report.py
.                                                                                                                                                    [100%]
------------------------------------------- generated xml file: D:\Code\Pytest\report\test_record_property3.xml -------------------------------------------
1 passed in 0.02s

生成的测试报告表现为:在testsuite节点中,多了一个properties子节点,包含所有新增的属性节点,而且,它和所有的testcase节点是平级的:

<?xml version="1.0" encoding="utf-8"?>
<testsuites>
    <testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.022" timestamp="2022-03-15T11:22:56.744263" hostname="LAPTOP-LEQLNICU">
        <properties>
            <property name="EXECUTOR" value="luizyao" />
            <property name="LOCATION" value="NJ" />
        </properties>
        <testcase classname="test_xml_report" name="test_record_property3" time="0.007" />
    </testsuite>
</testsuites>

注意:

这样生成的XML文件是符合最新的xunit标准的,这点和record_propertyrecord_xml_attribute正好相反;

21. 创建纯文本格式的测试报告

不推荐使用,计划在pytest6.0中删除这个功能

使用如下命令,可以在指定的path中创建一个纯文本的测试报告:

pytest --resultlog=path
22. 为测试报告提供URL链接–pastebin服务

目前,只实现了在http://bpaste.net上的展示功能;

  • 为每一个失败的测试用例创建一个URL

    pytest --pastebin=path
    

    也可以通过添加-x选项,只为第一个失败的测试用例创建一个URL;

  • 为所有的测试用例创建一个URL

    pytest --pastebin=all
    
23. 尽早的加载插件

你可以在命令行中使用-p选项,来尽早的加载某一个插件:

pytest -p mypluginmodule

-p选项接收一个name参数,这个参数可以为:

  • 一个完整的本地插件引入,例如:myproject.plugins,其必须是可以import的。

  • 一个公共插件的名称,这是其注册时在setuptools中赋予的名字,例如:尽早的加载pytest-cov插件:

    pytest -p pytest _cov
    
24. 去使能插件

你可以在命令行中使用-p结合no:,来去使能一个插件的加载,例如:

pytest -p no:doctest
25. 在Python代码中调用pytest

可以直接在代码中调用pytest

pytest.main()

这和你在命令行中执行pytest .几乎是一样的,但其也有以下特点:

  • 不会触发SystemExit,而是返回exitcode

    import time
    
    def test_one():
        time.sleep(10)
    
    
    if __name__ == "__main__":
        import pytest 
        ret = pytest.main(['-q',__file__])
        print("pytest.main() 返回pytest.ExitCode.INTERRUPTED:",ret==pytest.ExitCode.INTERRUPTED)
    

    用例中有等待10s的操作,在这期间,打断执行(Ctrl+C),pytest.main()返回的是INTERRUPTED状态码:

    > python .\invoke_via_main.py
    
    !!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!
    d:\Code\Pytest\invoke_via_main.py:4: KeyboardInterrupt
    (to show a full traceback on KeyboardInterrupt use --full-trace)
    no tests ran in 6.51s
    pytest.main() 返回pytest.ExitCode.INTERRUPTED: True
    
  • 传递选项和参数:

    pytest.main(["-x","mytestdir"])
    
  • 指定一个插件:

    import pytest
    
    class MyPlugin:
        def pytest_sessionfinish(self):
            print("*** test run reporting finishing")
        
    pytest.main(["-qq"],plugins=[MyPlugin()])
    

注意:

调用pytest.main()会引入你的测试文件以及其所引用的所有模块。用于Python引入机制的缓存特性,当这些文件发生变化时,后续再调用pytest.main()(在同一个程序执行过程中)时,并不会响应这些文件的变化。

基于这个原因,我们不推荐在同一个程序中多次调用pytest.main()(例如:为了重新执行测试;如果你确实有这个需求,或许可以考虑pytest-repeat插件)

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值