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()])
    
  • 相关阅读:
    BZOJ 3132: 上帝造题的七分钟 树状数组+差分
    PAT Advanced 1006 Sign In and Sign Out (25 分)
    PAT Advanced 1011 World Cup Betting (20 分)
    PAT Basic 1032 挖掘机技术哪家强 (20 分)
    PAT Basic 1028 人口普查 (20 分)
    PAT Basic 1004 成绩排名 (20 分)
    大数据数据库HBase(二)——搭建与JavaAPI
    PAT Advanced 1009 Product of Polynomials (25 分)(vector删除元素用的是erase)
    PAT Advanced 1002 A+B for Polynomials (25 分)(隐藏条件,多项式的系数不能为0)
    PAT Basic 1041 考试座位号 (15 分)
  • 原文地址:https://www.cnblogs.com/tedliu/p/13259771.html
Copyright © 2011-2022 走看看