zoukankan      html  css  js  c++  java
  • Pytest02-用法和调用

    通过python -m pytest调用pytest

    通过python解释器,在命令行执行:
    python -m pytest XXXX.py
    几乎等同于直接pytest XXXX.py,除了通过python会将当前目录添加到sys.path中。

    有可能会遇到的退出码

    运行pytest可能会导致六个不同的退出码:

    • 退出码0:所有测试均已收集并成功通过
    • 退出码1:测试已收集并运行,但是某些测试失败
    • 退出码2:测试执行被用户中断
    • 退出码3:执行测试时发生内部错误
    • 退出码4:pytest命令行使用错误
    • 退出码5:未收集测试
      它们由_pytest.config.ExitCode枚举表示。 退出码是公共API的一部分,可以使用以下命令直接导入和访问:
      from pytest import ExitCode
      注意:如果要在某些情况下自定义退出代码,尤其是在未收集任何测试的情况下,请考虑使用pytest-custom exit_code插件。

    获取相关信息

    pytest --version            # 获取版本号
    pytest --fixtures           # 获取可用的内置函数参数
    pytest -h / pytest --help   # 获取帮助文档
    

    最大测试失败数

    pytest -x             # 失败一个后就不再继续跑了 
    pytest --maxfail=2    # 失败两个后就不再继续跑了
    

    指定测试/选择测试

    先上代码:

    # -*- coding: utf-8 -*-
    # @Time    : 2020/7/7 10:59
    # @Author  : 无罪的坏人
    # @File    : test_mod.py
    import pytest
    
    
    def test_func():
        assert 1 == 1
    
    
    @pytest.mark.slow
    def test_slow():
        assert 2 == 3
    
    
    class TestClass:
        def test_method(self):
            assert 2 == 1
    
        @pytest.mark.parametrize('x,y', [(1, 2), (3, 4), (5, 5), (1, 1)])  # 实现参数化
        def test_equal(self, x, y):
            assert x == y
    

    运行某一个python模块

    pytest test_mod.py

    运行某一个目录

    pytest testing/

    运行包含XX关键字的但又不包含XXX关键字的用例

    pytest -k "MyClass and not method"
    上面这个命令:这个会运行TestMyClass.test_something,但是不会运行TestMyClass.test_method_simple.

    运行指定的nodeid的用例

    每个收集到的测试都分配有一个唯一的节点ID,该ID由模块文件名,后跟说明符(例如类名,函数名和参数化参数)组成,并由::字符分隔。
    比如:
    pytest test_mod.py::test_func # 模块名::方法名
    pytest test_mod.py::TestClass::test_method # 模块名::类名::方法名
    pytest test_mod.py::TestClass::test_equal[1-2] # 模块名::类名::方法名[参数1-参数2]
    注意:1.这里的参数要已经存在的,就是在参数化的元祖里面;2.中间用-连接。

    执行指定标记的用例:

    pytest -m slow
    测试用例可以用@pytest.mark.slow装饰即可。

    执行包里的测试用例

    pytest --pyargs pkg.testing
    pytest会导入pkg.testing,并且找到测试用例并执行。

    修改pytest的回溯信息

    pytest --showlocals    # 在回溯信息中打印本地变量
    pytest -l              # 在回溯信息中打印本地变量 (简短的)
    pytest --tb=auto       # (默认) 
    pytest --tb=long       # 详尽的回溯信息
    pytest --tb=short      # 更简短的回溯信息
    pytest --tb=line       # 每个失败信息总结在一行中
    pytest --tb=native     # Python标准库格式
    pytest --tb=no         # 屏蔽全部回溯信息
    

    PS:还有一个更加详细的模式:--full-trace(比--tb = long还要长)。如果测试花费的时间太长,而你却用Ctrl + C中断它们以找出测试的挂起位置。默认情况下,不会显示任何输出(因为pytest捕获了KeyboardInterrupt),通过使用此选项,可以确保显示跟踪。

    详细的总结报告

    -r参数可以在测试用例执行完后显示一个简短的测试摘要信息,从而使得在大型测试套件中可以比较清楚的看出哪些失败,哪些跳过。

    # -*- coding: utf-8 -*-
    # @Time    : 2020/7/7 13:58
    # @Author  : 无罪的坏人
    # @File    : test_example.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
    

    结果:

    >> pytest -ra test_example.py
    ================================================================================== test session starts ==================================================================================
    platform win32 -- Python 3.7.3, pytest-5.4.3, py-1.8.1, pluggy-0.13.1
    rootdir: D:PythonEDR2	estcase2
    plugins: allure-pytest-2.8.16, html-2.1.1, metadata-1.10.0, rerunfailures-9.0
    collected 6 items                                                                                                                                                                        
    
    test_example.py .FEsxX                                                                                                                                                             [100%]
    
    ======================================================================================== ERRORS =========================================================================================
    _____________________________________________________________________________ ERROR at setup of test_error ______________________________________________________________________________
    
        @pytest.fixture
        def error_fixture():
    >       assert 0
    E       assert 0
    
    test_example.py:11: AssertionError
    ======================================================================================= FAILURES ========================================================================================
    _______________________________________________________________________________________ test_fail _______________________________________________________________________________________
    
        def test_fail():
    >       assert 0
    E       assert 0
    
    test_example.py:19: AssertionError
    ================================================================================ short test summary info ================================================================================
    SKIPPED [1] D:PythonEDR2	estcase2	est_example.py:27: skipping this test
    XFAIL test_example.py::test_xfail
      reason: xfailing this test
    XPASS test_example.py::test_xpass always xfail
    ERROR test_example.py::test_error - assert 0
    FAILED test_example.py::test_fail - assert 0
    ========================================================= 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.18s =========================================================
    
    

    -r选项后面的a是什么含义呢?让我们再来看下。

    • f - 失败的
    • E - 报错的
    • s - 跳过的
    • x - 跳过执行并标记为xfailed的
    • X - 跳过执行并标记为xpassed的
    • p - 通过的
    • P - 通过并且有输出的
    • a - 除p和P外所有的
    • A - 所有的
    • N - 无
      上面这些参数可以结合在一起使用,比如:
      pytest -rfs
      可以在short test summary info看到失败的和跳过的。
      pytest -rpP P会把最后测试通过的用例输出捕获,就是那个ok
    ......
    ======================================================================================== PASSES =========================================================================================
    ________________________________________________________________________________________ test_ok ________________________________________________________________________________________
    --------------------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------------------
    ok
    ================================================================================ short test summary info ================================================================================
    PASSED test_example.py::test_ok
    ========================================================= 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.13s =========================================================
    
    

    用例失败时加载PDB(Python调试器)

    Python带有一个内置的称为PDB的Python调试器。 pytest允许通过以下命令进入PDB:
    pytest --pdb
    这将在每次失败(或KeyboardInterrupt)时调用Python调试器。 通常,你可能只想对第一个失败的测试执行此操作,以了解某些失败情况:
    pytest -x --pdb # 第一次失败就直接调用pdb
    pytest --pdb --maxfail=3 # 前面三次失败都调用pdb
    请注意,在发生任何故障时,异常信息都存储在sys.last_value,sys.last_type和sys.last_traceback中。在交互式使用中,你可以使用任何调试工具进行事后调试,也可以手动访问异常信息,例如:

    (Pdb) x  # 可以直接访问局部变量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 0x00000207F01933C8>
    (Pdb) sys.last_traceback.tb_lineno
    1477
    (Pdb) exit
    ....此处就退出Pdb了.....
    

    在每个用例执行时就加载PDB

    pytest --trace

    设置断点

    在你的测试代码中,写入import pdb;pdb.set_trace()就可以设置断点了。然后pytest会自动为该测试禁用其输出捕获

    • 其他测试中的输出捕获不受影响;
    • 结束调试就continue,就恢复输出捕获;

    使用内置断点功能

    Python 3.7有一个内置breakpoint()方法,pytest在以下场景支持:

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

    分析测试执行时间

    获取执行最慢的10个测试用例:
    pytest --durations=10
    通常,pytest不会展示时间小于0.01s的用例,除非用-vv

    错误处理

    5.0版本的新特性。
    Faulthandler标准模块可用于在segfault上或超时后转储Python跟踪。除非在命令行上给出-p no:faulthandler,否则该模块将自动启用pytest运行。
    如果测试花费的时间超过X秒(在Windows上不可用),则faulthandler_timeout = X配置选项也可以用于转储所有线程的回溯。
    注意:这个功能是从pytest-faulthandler插件合并而来的,但是有两点不同:

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

    创建JunitXML格式的文件

    通过以下命令,能创建一个可以被Jenkins或其他持续集成服务器读取的结果文件:
    pytest --junitxml=path
    你可以在配置文件pytest.ini里面设置junit_suite_name(4.0版本新增)的值,从而来改变junitxml文件中testsuite根节点的name值(默认是pytest):

    # pytest.ini
    [pytest]
    junit_suite_name = pytest_test
    

    添加新属性

    def test_function(record_property):
        record_property("example_key", 1)
        assert True
    

    报告中:

    <testcase classname="test_mod" file="test_mod.py" line="14" name="test_function" time="0.002">
          <properties>
                <property name="example_key" value="1"/>
          </properties>
    </testcase>
    

    或者,你也可以将此功能集成在自定义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))
    

    在测试用例中:

    @pytest.mark.test_id(1501)
    def test_function():
        assert True
    

    报告中:

    <testcase classname="test_mod" file="test_mod.py" line="19" name="test_function" time="0.002">
          <properties>
                <property name="test_id" value="1501"/>
          </properties>
    </testcase>
    

    警告:record_property是一个实验性功能,将来可能会发生变化。另外,这将破坏一些XML结构验证,与某些持续集成软件一起使用时,可能会导致一些问题。

    修改xml节点属性

    def test_function(self, record_xml_attribute):
        record_xml_attribute("assertions", "REQ-1234")
        record_xml_attribute("classname", "custom_classname")
        print("hello world")
        assert True
    

    与record_property不同, 它不会在节点下添加子元素,而是在生成的testcase标签内添加一个属性assertions ="REQ-1234",并使用classname = custom_classname覆盖默认的classname属性:

    <testcase assertions="REQ-1234" classname="custom_classname" file="test_mod.py" line="43" name="test_function" time="0.001"/>
    

    修改套件属性

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

    @pytest.fixture(scope="session", autouse=True)
    def log_global_env_facts(record_testsuite_property):
        record_testsuite_property("ARCH", "PPC")
        record_testsuite_property("STORAGE_TYPE", "CEPH")
    
    class TestMe:
        def test_foo(self):
            assert True
    

    报告中:

    <testsuites>
          <testsuite errors="0" failures="0" hostname="Liujin" name="pytest_chinese_doc" skipped="0" tests="2" time="1.048" timestamp="2020-07-07T16:28:45.192945">
                <properties>
                      <property name="ARCH" value="PPC"/>
                      <property name="STORAGE_TYPE" value="CEPH"/>
                </properties>
                <testcase classname="test_mod" file="test_mod.py" line="9" name="test_func" time="1.000"/>
                <testcase classname="test_mod.TestMe" file="test_mod.py" line="57" name="test_foo" time="0.000"/>
          </testsuite>
    </testsuites>
    

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

    创建结果日志格式文件

    使用如下命令,可以在指定的path中创建一个纯文本的测试报告(PS:不推荐使用,官方6.0就摒弃了):

    pytest --resultlog=path
    

    将测试报告发送到在线pastebin服务

    • 为每一个失败的测试用例创建一个URL
      pytest --pastebin=failed
      也可以通过添加-x选项,只为第一个失败的测试用例创建一个URL
    • 为所有的测试用例创建一个URL
      pytest --pastebin=all
      用例执行完后,会自动生成一个URL,点击它即可在浏览器中查看结果:
    ========================================================================== Sending information to Paste Service ===========================================================================
    test_mod.py:12: AssertionError --> https://bpaste.net/show/CF7A
    ================================================================================= short test summary info =================================================================================
    FAILED test_mod.py::test_func - assert 1 == 2
    ==================================================================================== 1 failed in 9.40s ====================================================================================
    
    

    尽早加载插件

    pytest -p mypluginmodule

    插件不能用

    pytest -p no:doctest

    在Python代码调用pytest

    pytest.main()

    • 这个方法和你直接在命令行执行pytest的效果几乎一样,只是不会触发SystemExit,而是返回exitcode;
    • 传递参数
      pytest.main(["-x", "mytestdir"])
    • 指定一个插件
    import pytest
    
    
    class MyPlugin:
        def pytest_sessionfinish(self):
            print("*** test run reporting finishing")
    
    
    pytest.main(["-qq"], plugins=[MyPlugin()])
    
  • 相关阅读:
    如何禁止在DBGRID末位自动添加一行记录
    DELPHI加密字串(异或运算加密)
    SQL SERVER 正则替换
    sql里的正则表达式
    UFIDA
    delphi raise 语句: 抛出异常
    delphi怎么一次性动态删除(释放)数个动态创建的组件?
    Delphi动态创建组件,并释放内存
    DELPHI 动态 创建和释放 多个 EDIT 控件
    禁止在DBGrid中按delete删除记录
  • 原文地址:https://www.cnblogs.com/tedliu/p/13259771.html
Copyright © 2011-2022 走看看