zoukankan      html  css  js  c++  java
  • 2、pytest -- 使用和调用

    1. 通过python -m pytest调用pytest

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

    python -m pytest [...]
    

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

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

    pytest命令执行结束,可能会返回以下六种状态码:

    • 0:(OK)所有收集到的用例测试通过
    • 1:(TESTS_FAILED)有用例测试失败
    • 2:(INTERRUPTED)用户打断测试执行
    • 3:(INTERNAL_ERROR)测试执行的过程中,发生内部错误
    • 4:(USAGE_ERROR)pytest命令使用错误
    • 5:(NO_TESTS_COLLECTED)没有收集到测试用例

    它们在枚举类 _pytest.main.ExitCode 中声明。并且,其作为公开API的一部分,能够直接引入和访问:

    from pytest import ExitCode
    

    3. 获取帮助信息

    pytest --version  # 查看版本号和pytest的引入路径
    pytest -h  # 查看帮助信息
    

    4. 最多允许失败的测试用例数

    当达到最大上限时,退出执行;如未配置,则没有上限:

    pytest -x  # 遇到第一个失败时,退出执行
    pytest --maxfail==2  # 遇到第二个失败时,退出执行
    

    5. 执行指定的测试用例

    pytest支持多种方式来执行特定的测试用例:

    5.1. 执行指定模块中的测试用例

    pytest test_mod.py
    

    5.2. 执行指定目录下所有的测试用例

    pytest testing/
    

    5.3. 执行文件名、类名或者函数名中包含特定关键字的测试用例

    执行当前目录下,名字包含_class但不包含two的测试用例:

    pytest -k "_class and not two" .
    

    注意:python的关键字不可以应用在-k选项中,例如,classdef等。

    5.4. 执行指定nodeid的测试用例

    pytest为每一个收集到的测试用例指定一个唯一的nodeid。其由模块名加说明符构成,中间以::间隔。

    其中,说明符可以是类名、函数名以及由parametrize标记赋予的参数

    # src/chapter-2/test_nodeid.py
    
    import pytest
    
    
    def test_one():
        print('test_one')
        assert 1
    
    
    class TestNodeId:
        def test_one(self):
            print('TestNodeId::test_one')
            assert 1
    
        @pytest.mark.parametrize('x,y', [(1, 1), (3, 4)])
        def test_two(self, x, y):
            print(f'TestNodeId::test_two::{x} == {y}')
            assert x == y
    

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

    • 指定函数名执行

      $ pipenv run pytest -q -s src/chapter-2/test_nodeid.py::test_one
      test_one
      .
      1 passed in 0.01s
      
    • 指定类名+函数名执行

      $ pipenv run pytest -q -s src/chapter-2/test_nodeid.py::TestNodeId::test_one
      TestNodeId::test_one
      .
      1 passed in 0.01s
      
    • 指定parametrize标记赋予的参数执行

      $ pipenv run pytest -q -s src/chapter-2/test_nodeid.py::TestNodeId::test_two[1-1]
      TestNodeId::test_two::1 == 1
      .
      1 passed in 0.01s
      

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

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

    注意:

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

    • 首先,可以使用--collect-only选项查看用例名:

      λ pipenv run pytest -q -s --collect-only src/chapter-2/test_nodeid.py
      test_nodeid.py::test_one
      test_nodeid.py::TestNodeId::test_one
      test_nodeid.py::TestNodeId::test_two[1-1]
      test_nodeid.py::TestNodeId::test_two[3-4]
      
    • 然后,使用-k执行符合规则的用例,例如:执行test_nodeid.py::test_one

      λ pipenv run pytest -q -s -k 'test_one and not TestNodeId' src/chapter-2/test_nodeid.py
      test_one
      .
      1 passed, 3 deselected in 0.02s
      

      结果和执行pipenv run pytest -q -s src/chapter-2/test_nodeid.py::test_one一样;

    5.5. 执行指定标记的用例

    pytest -m slow
    

    5.6. 执行指定包中的测试用例

    pytest --pyargs pkg.testing
    

    pytest会引入pkg.testing包,并在它的系统目录下搜寻测试用例并执行;

    6. 修改回溯信息的输出模式

    pytest回溯信息的输出一共有六种模式:auto/long/short/line/native/no,用--tb选项指定:

    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)时的回溯信息,而上述六种模式默认是不输出此类信息的。

    7. 总结报告

    -r选项可以在执行结束后,打印一个简短的总结报告。在执行的测试用例很多时,可以让你对结果有个清晰的了解:

    # src/chapter-2/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.skip("skipping this test")
    
    
    def test_xfail():
        pytest.xfail("xfailing this test")
    
    
    @pytest.mark.xfail(reason="always xfail")
    def test_xpass():
        pass
    
    $ pipenv run pytest -q -rA src/chapter-2/test_report.py 
    .FEsxX                                                            [100%]
    ================================ ERRORS =================================
    _____________________ ERROR at setup of test_error ______________________
    
        @pytest.fixture
        def error_fixture():
    >       assert 0
    E       assert 0
    
    src/chapter-2/test_report.py:27: AssertionError
    =============================== FAILURES ================================
    _______________________________ test_fail _______________________________
    
        def test_fail():
    >       assert 0
    E       assert 0
    
    src/chapter-2/test_report.py:35: AssertionError
    ================================ PASSES =================================
    ________________________________ test_ok ________________________________
    ------------------------- Captured stdout call --------------------------
    ok
    ======================== short test summary info ========================
    PASSED src/chapter-2/test_report.py::test_ok
    SKIPPED [1] /Users/yaomeng/Private/Projects/pytest-chinese-doc/src/chapter-2/test_report.py:44: skipping this test
    XFAIL src/chapter-2/test_report.py::test_xfail
      reason: xfailing this test
    XPASS src/chapter-2/test_report.py::test_xpass always xfail
    ERROR src/chapter-2/test_report.py::test_error - assert 0
    FAILED src/chapter-2/test_report.py::test_fail - assert 0
    1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.08s
    

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

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

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

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

    pytest -rfs
    

    8. 失败时加载PDB(Python Debugger)环境

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

    pytest --pdb
    

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

    # src/chapter-2/test_pdb.py
    
    def test_fail():
        x = 1
        assert x == 0
    
    $ pipenv run pytest -q --pdb src/chapter-2/test_pdb.py 
    F
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    
        def test_fail():
            x = 1
    >       assert x == 0
    E       assert 1 == 0
    
    src/chapter-2/test_pdb.py:25: AssertionError
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    
    >>>>>>>>>>>>>>> PDB post_mortem (IO-capturing turned off) >>>>>>>>>>>>>>>
    > /Users/yaomeng/Private/Projects/pytest-chinese-doc/src/chapter-2/test_pdb.py(25)test_fail()
    -> assert x == 0
    (Pdb) 
    (Pdb) x
    1
    (Pdb) 
    (Pdb) import sys
    (Pdb) sys.last_value
    AssertionError('assert 1 == 0')
    (Pdb) sys.last_type
    <class 'AssertionError'>
    (Pdb) sys.last_traceback
    <traceback object at 0x1077ec808>
    

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

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

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

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

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

    pytest --trace
    

    10. 设置断点

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

    • 其他用例不受影响;
    • 通过continue命令,退出PDB环境,并继续执行用例;

    11. 使用内置的中断函数

    python 3.7介绍了一个内置breakpoint()函数。pytest可以在以下场景中支持使用:

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

    12. 分析测试执行时间

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

    pytest --durations=10
    

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

    13. 错误句柄

    5.0版本新增特性

    在测试执行中发生段错误或者超时的情况下,faulthandler标准模块可以转储python的回溯信息;

    它在pytest的执行中默认使能,使用-p no:faulthandler选项可以关闭它;

    同样,faulthandler_timeout=X配置项,可用于当测试用例的完成时间超过X秒时,转储所有线程的python回溯信息:

    # src/chapter-2/pytest.ini
    
    [pytest]
    faulthandler_timeout=5
    

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

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

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

    • 默认使能faulthandler的情况:

      $ pipenv run pytest -q src/chapter-2/test_faulthandler.py 
      Timeout (0:00:05)!
      Thread 0x000000010ff275c0 (most recent call first):
        File "/Users/yaomeng/Private/Projects/pytest-chinese-doc/src/chapter-2/test_faulthandler.py", line 26 in test_faulthandler
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/python.py", line 170 in pytest_pyfunc_call
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/callers.py", line 187 in _multicall
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 86 in <lambda>
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 92 in _hookexec
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/hooks.py", line 286 in __call__
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/python.py", line 1423 in runtest
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 117 in pytest_runtest_call
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/callers.py", line 187 in _multicall
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 86 in <lambda>
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 92 in _hookexec
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/hooks.py", line 286 in __call__
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 192 in <lambda>
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 220 in from_call
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 192 in call_runtest_hook
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 167 in call_and_report
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 87 in runtestprotocol
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/runner.py", line 72 in pytest_runtest_protocol
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/callers.py", line 187 in _multicall
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 86 in <lambda>
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 92 in _hookexec
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/hooks.py", line 286 in __call__
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/main.py", line 256 in pytest_runtestloop
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/callers.py", line 187 in _multicall
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 86 in <lambda>
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 92 in _hookexec
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/hooks.py", line 286 in __call__
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/main.py", line 235 in _main
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/main.py", line 191 in wrap_session
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/main.py", line 228 in pytest_cmdline_main
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/callers.py", line 187 in _multicall
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 86 in <lambda>
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/manager.py", line 92 in _hookexec
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/pluggy/hooks.py", line 286 in __call__
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/lib/python3.7/site-packages/_pytest/config/__init__.py", line 77 in main
        File "/Users/yaomeng/.local/share/virtualenvs/pytest-chinese-doc-EK3zIUmM/bin/pytest", line 10 in <module>
      .                                                                 [100%]
      1 passed in 7.02s
      

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

    • 去使能faulthandler的情况:

      $ pipenv run pytest -q -p no:faulthandler src/chapter-2/test_faulthandler.py 
      .                                                                 [100%]
      1 passed in 7.02s
      

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

    注意:

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

    • 去使能时,使用-p no:faulthandler代替原来的--no-faulthandler;
    • 使用faulthandler_timeout 配置项代替--faulthandler-timeout命令行选项来配置超时时间。当然,你也可以使用-o faulthandler_timeout=X在命令行配置;

    14. 创建JUnitXML格式的测试报告

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

    pytest --junitxml=path
    

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

    junit_suite_name是4.0版本新增的配置项;

    # src/chapter-2/pytest.ini
    
    [pytest]
    junit_suite_name = pytest_chinese_doc
    

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

    pipenv run pytest -q --junitxml=src/chapter-2/report/test_one.xml src/chapter-2/test_nodeid.py::test_one
    

    生成的XML测试报告:

    <?xml version="1.0" encoding="utf-8"?>
    <testsuites>
      <testsuite errors="0" failures="0" hostname="NJ-LUYAO-T460" name="pytest_chinese_doc" skipped="0" tests="1"
        time="0.030" timestamp="2019-09-27T14:33:32.459788">
        <testcase classname="test_nodeid" file="test_nodeid.py" line="24" name="test_one" time="0.002">
          <system-out>test_one
          </system-out>
        </testcase>
      </testsuite>
    </testsuites>
    

    我们可以看到,<testsuite>节点的name属性的值,变为我们所期望的pytest_chinese_doc,而不是默认的pytest

    JUnit XML规定time属性应该表明测试用例执行的全部耗时,包含setupteardown中的操作,这也是pytest的默认行为;

    如果你只想记录测试用例执行的时间,只需要做如下配置:

    # src/chapter-2/pytest.ini
    
    junit_duration_report = call
    

    15. 在报告中为测试用例附加额外的子节点信息

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

    • 使用record_property fixture

      test_record_property用例添加一个额外的test_id

      # src/chapter-2/test_xml_report.py
      
      def test_record_property(record_property):
          record_property("test_id", 10010)
          assert 1
      

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

      <!-- src/chapter-2/report/test_record_property.xml -->
      
      <?xml version="1.0" encoding="utf-8"?>
      <testsuites>
        <testsuite errors="0" failures="0" hostname="NJ-LUYAO-T460" name="pytest_chinese_doc" skipped="0" tests="1"
          time="0.024" timestamp="2019-09-27T15:02:41.277369">
          <testcase classname="test_xml_report" file="test_xml_report.py" line="22" name="test_record_property" time="0.002">
            <properties>
              <property name="test_id" value="10010" />
            </properties>
          </testcase>
        </testsuite>
      </testsuites>
      
    • 解析一个自定义的标记@pytest.mark.test_id():

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

      # src/chapter-2/conftest.py
      
      def pytest_collection_modifyitems(session, config, items):
          for item in items:
              for marker in item.iter_markers(name="test_id"):
                  test_id = marker.args[0]
                  item.user_properties.append(("test_id", test_id))
      

      然后,修改测试用例:

      # src/chapter-2/test_xml_report.py
      
      import pytest
      
      @pytest.mark.test_id(10010)
      def test_record_property1():
          assert 1
      

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

      <!-- src/chapter-2/report/test_record_property1.xml -->
      
      <?xml version="1.0" encoding="utf-8"?>
      <testsuites>
        <testsuite errors="0" failures="0" hostname="NJ-LUYAO-T460" name="pytest_chinese_doc" skipped="0" tests="1"
          time="0.029" timestamp="2019-09-27T15:16:05.309308">
          <testcase classname="test_xml_report" file="test_xml_report.py" line="29" name="test_record_property1" time="0.001">
            <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/latest/mark.html

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

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

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

    注意:

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

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

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

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

    # src/chapter-2/test_xml_report.py
    
    def test_record_property2(record_xml_attribute):
        record_xml_attribute('test_id', 10010)
        record_xml_attribute('classname', 'custom_classname')
        assert 1
    

    在报告中的表现为<testcase classname="custom_classname" test_id="10010" ...

    <!-- src/chapter-2/report/test_record_property2.xml -->
    
    <?xml version="1.0" encoding="utf-8"?>
    <testsuites>
      <testsuite errors="0" failures="0" hostname="NJ-LUYAO-T460" name="pytest_chinese_doc" skipped="0" tests="1"
        time="0.028" timestamp="2019-09-27T15:35:47.093494">
        <testcase classname="custom_classname" file="test_xml_report.py" line="34" name="test_record_property2"
          test_id="10010" time="0.001"></testcase>
      </testsuite>
    </testsuites>
    

    注意:

    • record_xml_attribute目前是一个实验性的功能,未来可能被更强大的API所替代,但功能本身会被保留。

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

    15.2. 在报告中为测试集附加额外的子节点信息

    4.5版本新增功能

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

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

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

    # src/chapter-2/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):
        assert 1
    

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

    <!-- src/chapter-2/report/test_record_property3.xml -->
    
    <?xml version="1.0" encoding="utf-8"?>
    <testsuites>
      <testsuite errors="0" failures="0" hostname="NJ-LUYAO-T460" name="pytest_chinese_doc" skipped="0" tests="1"
        time="0.027" timestamp="2019-09-27T15:52:34.562238">
        <properties>
          <property name="EXECUTOR" value="luizyao" />
          <property name="LOCATION" value="NJ" />
        </properties>
        <testcase classname="test_xml_report" file="test_xml_report.py" line="46" name="test_record_property3" time="0.002">
        </testcase>
      </testsuite>
    </testsuites>
    

    注意:

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

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

    不推荐使用,计划在pytest 6.0中删除这个功能

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

    pytest --resultlog=path
    

    17. 为测试报告提供URL链接 -- pastebin服务

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

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

      pytest --pastebin=failed
      

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

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

      pytest --pastebin=all
      

    18. 尽早的加载插件

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

    pytest -p mypluginmodule
    

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

    • 一个完整的本地插件引入,例如:myproject.plugins,其必须是可以import的。
    • 一个公共插件的名称,这是其注册时在setuptools中赋予的名字,例如:尽早的加载pytest-cov插件:
      pytest -p pytest_cov
      

    19. 去使能插件

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

    pytest -p no:doctest
    

    20. python代码中调用pytest

    可以直接在代码中调用pytest

    pytest.main()
    

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

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

      # src/chapter-2/invoke_via_main.py
      
      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)
      

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

      λ pipenv run python src/chapter-2/invoke_via_main.py
      
      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
      D:Personal FilesProjectspytest-chinese-docsrcchapter-2invoke_via_main.py:26: KeyboardInterrupt
      (to show a full traceback on KeyboardInterrupt use --full-trace)
      no tests ran in 1.04s
      pytest.main() 返回 pytest.ExitCode.INTERRUPTED: True
      
      Aborted!
      
    • 传递选项和参数:

      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插件);

    GitHub仓库地址:https://github.com/luizyao/pytest-chinese-doc

  • 相关阅读:
    高情商人士7大说话之道
    使用httpclient提交表单数据加号(+)会被自动替换成空格的坑
    鬼谷子的五条初世潜规则
    模型可视化工具netron
    .deb文件安装应该怎么做
    转caffe scale layer
    转Ubuntu 16.04 创建无线热点
    CNN反向传播更新权值
    tensorflow查看使用的是cpu还是gpu
    yolo进化史之yolov3
  • 原文地址:https://www.cnblogs.com/luizyao/p/11498169.html
Copyright © 2011-2022 走看看