python第三方测试框架pytest

时间:2020-05-17 21:14:54   收藏:0   阅读:419

Pytest vs Unittest

环境配置

介绍

pytest 是一个非常成熟的 Python 测试框架,可以做到做个场景的测试工作,如:单元测试、接口测试、web测试等。

pytest 是一个插件化平台,这就是它比 unittest 强大的原因,丰富的插件扩展增强了它的功能,也可以根据自己的需要定制化开发自己的插件,非常的灵活。

安装

# 安装 pytest 
# !pip3 install pytest
Collecting pytest
  Downloading http://mirrors.tencentyun.com/pypi/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
    100% |████████████████████████████████| 256kB 1.6MB/s ta 0:00:01
[?25hCollecting more-itertools>=4.0.0 (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 48.1MB/s ta 0:00:01
[?25hCollecting packaging (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting py>=1.5.0 (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
    100% |████████████████████████████████| 92kB 2.8MB/s ta 0:00:011
[?25hCollecting pluggy<1.0,>=0.12 (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting attrs>=17.4.0 (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting wcwidth (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
    100% |████████████████████████████████| 71kB 2.2MB/s eta 0:00:01
[?25hCollecting six (from packaging->pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest)
  Downloading http://mirrors.tencentyun.com/pypi/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Installing collected packages: more-itertools, pyparsing, six, packaging, py, zipp, importlib-metadata, pluggy, attrs, wcwidth, pytest
Successfully installed attrs-19.3.0 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0
# 查看 pytest 是否安装成功
# !pytest --version
This is pytest version 5.4.1, imported from /home/ubuntu/.local/lib/python3.6/site-packages/pytest/__init__.py
# 显示可用的内置参数
# !pytest --fixtures
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 0 items                                                              
cache
    Return a cache object that can persist state between testing sessions.
    
    cache.get(key, default)
    cache.set(key, value)
    
    Keys must be a ``/`` separated value, where the first part is usually the
    name of your plugin or application to avoid clashes with other cache users.
    
    Values can be any object handled by the json stdlib module.

capsys
    Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.
    
    The captured output is made available via ``capsys.readouterr()`` method
    calls, which return a ``(out, err)`` namedtuple.
    ``out`` and ``err`` will be ``text`` objects.

capsysbinary
    Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.
    
    The captured output is made available via ``capsysbinary.readouterr()``
    method calls, which return a ``(out, err)`` namedtuple.
    ``out`` and ``err`` will be ``bytes`` objects.

capfd
    Enable text capturing of writes to file descriptors ``1`` and ``2``.
    
    The captured output is made available via ``capfd.readouterr()`` method
    calls, which return a ``(out, err)`` namedtuple.
    ``out`` and ``err`` will be ``text`` objects.

capfdbinary
    Enable bytes capturing of writes to file descriptors ``1`` and ``2``.
    
    The captured output is made available via ``capfd.readouterr()`` method
    calls, which return a ``(out, err)`` namedtuple.
    ``out`` and ``err`` will be ``byte`` objects.

doctest_namespace [session scope]
    Fixture that returns a :py:class:`dict` that will be injected into the namespace of doctests.

pytestconfig [session scope]
    Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
    
    Example::
    
        def test_foo(pytestconfig):
            if pytestconfig.getoption("verbose") > 0:
                ...

record_property
    Add an extra properties the calling test.
    User properties become part of the test report and are available to the
    configured reporters, like JUnit XML.
    The fixture is callable with ``(name, value)``, with value being automatically
    xml-encoded.
    
    Example::
    
        def test_function(record_property):
            record_property("example_key", 1)

record_xml_attribute
    Add extra xml attributes to the tag for the calling test.
    The fixture is callable with ``(name, value)``, with value being
    automatically xml-encoded

record_testsuite_property [session scope]
    Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to
    writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family.
    
    This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:
    
    .. code-block:: python
    
        def test_foo(record_testsuite_property):
            record_testsuite_property("ARCH", "PPC")
            record_testsuite_property("STORAGE_TYPE", "CEPH")
    
    ``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.

caplog
    Access and control log capturing.
    
    Captured logs are available through the following properties/methods::
    
    * caplog.messages        -> list of format-interpolated log messages
    * caplog.text            -> string containing formatted log output
    * caplog.records         -> list of logging.LogRecord instances
    * caplog.record_tuples   -> list of (logger_name, level, message) tuples
    * caplog.clear()         -> clear captured records and formatted log output string

monkeypatch
    The returned ``monkeypatch`` fixture provides these
    helper methods to modify objects, dictionaries or os.environ::
    
        monkeypatch.setattr(obj, name, value, raising=True)
        monkeypatch.delattr(obj, name, raising=True)
        monkeypatch.setitem(mapping, name, value)
        monkeypatch.delitem(obj, name, raising=True)
        monkeypatch.setenv(name, value, prepend=False)
        monkeypatch.delenv(name, raising=True)
        monkeypatch.syspath_prepend(path)
        monkeypatch.chdir(path)
    
    All modifications will be undone after the requesting
    test function or fixture has finished. The ``raising``
    parameter determines if a KeyError or AttributeError
    will be raised if the set/deletion operation has no target.

recwarn
    Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions.
    
    See http://docs.python.org/library/warnings.html for information
    on warning categories.

tmpdir_factory [session scope]
    Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.
        

tmp_path_factory [session scope]
    Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.
        

tmpdir
    Return a temporary directory path object
    which is unique to each test function invocation,
    created as a sub directory of the base temporary
    directory.  The returned object is a `py.path.local`_
    path object.
    
    .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html

tmp_path
    Return a temporary directory path object
    which is unique to each test function invocation,
    created as a sub directory of the base temporary
    directory.  The returned object is a :class:`pathlib.Path`
    object.
    
    .. note::
    
        in python < 3.6 this is a pathlib2.Path


============================ no tests ran in 0.03s =============================

执行用例

执行退出码

0 -- 成功地收集并传递了所有测试
1 -- 测试被收集和运行但一些测试失败

2 -- 测试执行被用户中断
3 -- 执行测试时发生内部错误
4 -- pytest 命令行使用错误
5 -- 未收集任何测试

执行测试文件

"""
示例文件
@FileName: test_start.py
"""

def func(x):
    return x + 1

def test_func():
    assert func(3) == 5
    
class TestClass:
    def test_one(self):
        x = "This"
        assert "h" in x
        
    def test_two(self):
        x = "hello"
        assert hasattr(x, "check")

执行某个目录下所有的用例

!pytest 
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

test_start.py F.F                                                        [100%]

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_start.py:8: AssertionError
______________________________ TestClass.test_two ______________________________

self = <test_start.TestClass object at 0x7f384557fc18>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

test_start.py:17: AssertionError
=========================== short test summary info ============================
FAILED test_start.py::test_func - assert 4 == 5
FAILED test_start.py::TestClass::test_two - AssertionError: assert False
========================= 2 failed, 1 passed in 0.12s ==========================

执行某个 py 文件下用例

!pytest start.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

start.py F.F                                                             [100%]

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
______________________________ TestClass.test_two ______________________________

self = <start.TestClass object at 0x7f0d00651278>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

start.py:17: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
FAILED start.py::TestClass::test_two - AssertionError: assert False
========================= 2 failed, 1 passed in 0.12s ==========================

执行 py 文件中的某个函数

!pytest start.py::test_func
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

start.py F                                                               [100%]

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
============================== 1 failed in 0.10s ===============================

执行 py 文件中的某个类

!pytest start.py::TestClass
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

start.py .F                                                              [100%]

=================================== FAILURES ===================================
______________________________ TestClass.test_two ______________________________

self = <start.TestClass object at 0x7f5fc75cb160>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

start.py:17: AssertionError
=========================== short test summary info ============================
FAILED start.py::TestClass::test_two - AssertionError: assert False
========================= 1 failed, 1 passed in 0.13s ==========================

执行 py 文件中类里的某个方法

!pytest start.py::TestClass::test_one
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

start.py .                                                               [100%]

============================== 1 passed in 0.01s ===============================

-v 显示每个测试函数结果

"""显示每一个测试函数的执行结果"""
!pytest -v start.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

start.py::test_func FAILED                                               [ 33%]
start.py::TestClass::test_one PASSED                                     [ 66%]
start.py::TestClass::test_two FAILED                                     [100%]

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E         +4
E         -5

start.py:8: AssertionError
______________________________ TestClass.test_two ______________________________

self = <start.TestClass object at 0x7fc060a955c0>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

start.py:17: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
FAILED start.py::TestClass::test_two - AssertionError: assert False
========================= 2 failed, 1 passed in 0.16s ==========================

-m 标记表达式

# 将运行用 @pytest.mark.login 装饰器修饰的所有测试
!pytest -m login 
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 4 items / 4 deselected                                               

============================ 4 deselected in 0.04s =============================

-q 简单但因,只打印测试用例的执行结果

!pytest -q start.py
F.F                                                                      [100%]
=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
______________________________ TestClass.test_two ______________________________

self = <start.TestClass object at 0x7fa6e310a4a8>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

start.py:17: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
FAILED start.py::TestClass::test_two - AssertionError: assert False
2 failed, 1 passed in 0.16s

-s 详细打印

!pytest -s start.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

start.py F.F

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
______________________________ TestClass.test_two ______________________________

self = <start.TestClass object at 0x7f087090bd30>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

start.py:17: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
FAILED start.py::TestClass::test_two - AssertionError: assert False
========================= 2 failed, 1 passed in 0.12s ==========================

-x 遇到错误时停止测试

!pytest start.py -x
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

start.py F

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
============================== 1 failed in 0.11s ===============================

--maxfail=num,当用例错误个数达到指定数量是,停止测试

!pytest start.py --maxfail=1
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

start.py F

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
============================== 1 failed in 0.12s ===============================

-k 匹配用例名称

# 执行测试用例名称包含http的所有用例
!pytest -s -k http start.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items / 3 deselected                                               

============================ 3 deselected in 0.01s =============================

-k 根据用例名称排除某些用例

!pytest -s -k "not http" start.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

start.py F.F

=================================== FAILURES ===================================
__________________________________ test_func ___________________________________

    def test_func():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

start.py:8: AssertionError
______________________________ TestClass.test_two ______________________________

self = <start.TestClass object at 0x7f946a3cfe80>

    def test_two(self):
        x = "hello"
>       assert hasattr(x, "check")
E       AssertionError: assert False
E        +  where False = hasattr(‘hello‘, ‘check‘)

start.py:17: AssertionError
=========================== short test summary info ============================
FAILED start.py::test_func - assert 4 == 5
FAILED start.py::TestClass::test_two - AssertionError: assert False
========================= 2 failed, 1 passed in 0.12s ==========================

-k 同时匹配不同的用例名称

!pytest -s -k "method or weibo" start.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items / 3 deselected                                               

============================ 3 deselected in 0.01s =============================

断言 assert

常用断言

常用断言方法:

import pytest

def f():
    return 3

def test_function():
    a = f()
    assert a % 2 == 0, "判断 a 为偶数,当前 a 的值为:%s" % a
    
def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0
        
!pytest assert.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

assert.py F.                                                             [100%]

=================================== FAILURES ===================================
________________________________ test_function _________________________________

    def test_function():
        a = f()
>       assert a % 2 == 0, "判断 a 为偶数,当前 a 的值为:%s" % a
E       AssertionError: 判断 a 为偶数,当前 a 的值为:3
E       assert (3 % 2) == 0

assert.py:8: AssertionError
=========================== short test summary info ============================
FAILED assert.py::test_function - AssertionError: 判断 a 为偶数,当前 a 的值...
========================= 1 failed, 1 passed in 0.11s ==========================

异常断言

可以使用 pytest.raises 作为上下文管理器,当抛出异常时可以获取到对应的异常实例

"""异常断言"""
import pytest

def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0
  
"""详细断言异常"""
def test_zero_division_long_info():
    with pytest.raises(ZeroDivisionError) as excinfo:
        1 / 0
        
    """断言异常类型"""
    assert excinfo.type == ZeroDivisionError
    
    """断言异常 value 的值"""
    assert "Division by Zero" in str(excinfo.value)
        

!pytest assert_except.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

assert_except.py .F                                                      [100%]

=================================== FAILURES ===================================
_________________________ test_zero_division_long_info _________________________

    def test_zero_division_long_info():
        with pytest.raises(ZeroDivisionError) as excinfo:
            1 / 0
    
        """断言异常类型"""
        assert excinfo.type == ZeroDivisionError
    
        """断言异常 value 的值"""
>       assert "Division by Zero" in str(excinfo.value)
E       AssertionError: assert ‘Division by Zero‘ in ‘division by zero‘
E        +  where ‘division by zero‘ = str(ZeroDivisionError(‘division by zero‘,))
E        +    where ZeroDivisionError(‘division by zero‘,) = <ExceptionInfo ZeroDivisionError(‘division by zero‘,) tblen=1>.value

assert_except.py:17: AssertionError
=========================== short test summary info ============================
FAILED assert_except.py::test_zero_division_long_info - AssertionError: asser...
========================= 1 failed, 1 passed in 0.11s ==========================

match

"""自定义消息"""
def test_zero_division_custom_incinfo():
    with pytest.raises(ZeroDivisionError, match="*.zero.*") as excinfo:
        1 / 0
        

!pytest assert_except.py::test_zero_division_custom_incinfo
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

assert_except.py F                                                       [100%]

=================================== FAILURES ===================================
______________________ test_zero_division_custom_incinfo _______________________

    def test_zero_division_custom_incinfo():
        with pytest.raises(ZeroDivisionError, match="*.zero.*") as excinfo:
>           1 / 0
E           ZeroDivisionError: division by zero

assert_except.py:23: ZeroDivisionError

During handling of the above exception, another exception occurred:

    def test_zero_division_custom_incinfo():
        with pytest.raises(ZeroDivisionError, match="*.zero.*") as excinfo:
>           1 / 0

assert_except.py:23: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.6/re.py:182: in search
    return _compile(pattern, flags).search(string)
/usr/lib/python3.6/re.py:301: in _compile
    p = sre_compile.compile(pattern, flags)
/usr/lib/python3.6/sre_compile.py:562: in compile
    p = sre_parse.parse(p, flags)
/usr/lib/python3.6/sre_parse.py:855: in parse
    p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, 0)
/usr/lib/python3.6/sre_parse.py:416: in _parse_sub
    not nested and not items))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

source = <sre_parse.Tokenizer object at 0x7f7aac6d9630>
state = <sre_parse.Pattern object at 0x7f7aac663390>, verbose = 0, nested = 1
first = True

    def _parse(source, state, verbose, nested, first=False):
        # parse a simple pattern
        subpattern = SubPattern(state)
    
        # precompute constants into local variables
        subpatternappend = subpattern.append
        sourceget = source.get
        sourcematch = source.match
        _len = len
        _ord = ord
    
        while True:
    
            this = source.next
            if this is None:
                break # end of pattern
            if this in "|)":
                break # end of subpattern
            sourceget()
    
            if verbose:
                # skip whitespace and comments
                if this in WHITESPACE:
                    continue
                if this == "#":
                    while True:
                        this = sourceget()
                        if this is None or this == "\n":
                            break
                    continue
    
            if this[0] == "\\":
                code = _escape(source, this, state)
                subpatternappend(code)
    
            elif this not in SPECIAL_CHARS:
                subpatternappend((LITERAL, _ord(this)))
    
            elif this == "[":
                here = source.tell() - 1
                # character set
                set = []
                setappend = set.append
    ##          if sourcematch(":"):
    ##              pass # handle character classes
                if sourcematch("^"):
                    setappend((NEGATE, None))
                # check remaining characters
                start = set[:]
                while True:
                    this = sourceget()
                    if this is None:
                        raise source.error("unterminated character set",
                                           source.tell() - here)
                    if this == "]" and set != start:
                        break
                    elif this[0] == "\\":
                        code1 = _class_escape(source, this)
                    else:
                        code1 = LITERAL, _ord(this)
                    if sourcematch("-"):
                        # potential range
                        that = sourceget()
                        if that is None:
                            raise source.error("unterminated character set",
                                               source.tell() - here)
                        if that == "]":
                            if code1[0] is IN:
                                code1 = code1[1][0]
                            setappend(code1)
                            setappend((LITERAL, _ord("-")))
                            break
                        if that[0] == "\\":
                            code2 = _class_escape(source, that)
                        else:
                            code2 = LITERAL, _ord(that)
                        if code1[0] != LITERAL or code2[0] != LITERAL:
                            msg = "bad character range %s-%s" % (this, that)
                            raise source.error(msg, len(this) + 1 + len(that))
                        lo = code1[1]
                        hi = code2[1]
                        if hi < lo:
                            msg = "bad character range %s-%s" % (this, that)
                            raise source.error(msg, len(this) + 1 + len(that))
                        setappend((RANGE, (lo, hi)))
                    else:
                        if code1[0] is IN:
                            code1 = code1[1][0]
                        setappend(code1)
    
                # XXX: <fl> should move set optimization to compiler!
                if _len(set)==1 and set[0][0] is LITERAL:
                    subpatternappend(set[0]) # optimization
                elif _len(set)==2 and set[0][0] is NEGATE and set[1][0] is LITERAL:
                    subpatternappend((NOT_LITERAL, set[1][1])) # optimization
                else:
                    # XXX: <fl> should add charmap optimization here
                    subpatternappend((IN, set))
    
            elif this in REPEAT_CHARS:
                # repeat previous item
                here = source.tell()
                if this == "?":
                    min, max = 0, 1
                elif this == "*":
                    min, max = 0, MAXREPEAT
    
                elif this == "+":
                    min, max = 1, MAXREPEAT
                elif this == "{":
                    if source.next == "}":
                        subpatternappend((LITERAL, _ord(this)))
                        continue
                    min, max = 0, MAXREPEAT
                    lo = hi = ""
                    while source.next in DIGITS:
                        lo += sourceget()
                    if sourcematch(","):
                        while source.next in DIGITS:
                            hi += sourceget()
                    else:
                        hi = lo
                    if not sourcematch("}"):
                        subpatternappend((LITERAL, _ord(this)))
                        source.seek(here)
                        continue
                    if lo:
                        min = int(lo)
                        if min >= MAXREPEAT:
                            raise OverflowError("the repetition number is too large")
                    if hi:
                        max = int(hi)
                        if max >= MAXREPEAT:
                            raise OverflowError("the repetition number is too large")
                        if max < min:
                            raise source.error("min repeat greater than max repeat",
                                               source.tell() - here)
                else:
                    raise AssertionError("unsupported quantifier %r" % (char,))
                # figure out which item to repeat
                if subpattern:
                    item = subpattern[-1:]
                else:
                    item = None
                if not item or (_len(item) == 1 and item[0][0] is AT):
                    raise source.error("nothing to repeat",
>                                      source.tell() - here + len(this))
E                   sre_constants.error: nothing to repeat at position 0

/usr/lib/python3.6/sre_parse.py:616: error
=========================== short test summary info ============================
FAILED assert_except.py::test_zero_division_custom_incinfo - sre_constants.er...
============================== 1 failed in 0.26s ===============================

检查断言装饰器

"""异常装饰器"""
@pytest.mark.xfail(raises=ZeroDivisionError)
def test_except_decorate():
    1 / 0
    

!pytest assert_except.py::test_except_decorate
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

assert_except.py x                                                       [100%]

============================== 1 xfailed in 0.03s ==============================

setup 和 teardown

注意:setup、teardown可以实现在执行用例前或结束后加入一些操作,但这种都是针对整个脚本全局生效的

"""前置后置条件"""

import pytest

def setup_module():
    print("=====整个.py模块开始前只执行一次:打开浏览器=====")
    
def teardown_module():
    print("=====整个.py模块结束后只执行一次:关闭浏览器=====")
 

def setup_function():
    print("===每个函数级别用例开始前都执行setup_function===")

def teardown_function():
    print("===每个函数级别用例结束后都执行teardown_function====")
    
    
def test_one():
    print("one")

def test_two():
    print("two")
    
    
class TestCase():
    def setup_class(self):
        print("====整个测试类开始前只执行一次setup_class====")

    def teardown_class(self):
        print("====整个测试类结束后只执行一次teardown_class====")

    def setup_method(self):
        print("==类里面每个用例执行前都会执行setup_method==")

    def teardown_method(self):
        print("==类里面每个用例结束后都会执行teardown_method==")

    def setup(self):
        print("=类里面每个用例执行前都会执行setup=")

    def teardown(self):
        print("=类里面每个用例结束后都会执行teardown=")

    def test_three(self):
        print("three")
        
def test_four(self):
        print("four")
        

if __name__ == ‘__main__‘:
    pytest.main(["-q", "-s", "-ra", "setup_teardown.py"])
    
    
!pytest setup_teardown.py
=====整个.py模块开始前只执行一次:打开浏览器=====
===每个函数级别用例开始前都执行setup_function===
one
.===每个函数级别用例结束后都执行teardown_function====
===每个函数级别用例开始前都执行setup_function===
two
.===每个函数级别用例结束后都执行teardown_function====
====整个测试类开始前只执行一次setup_class====
==类里面每个用例执行前都会执行setup_method==
=类里面每个用例执行前都会执行setup=
three
.=类里面每个用例结束后都会执行teardown=
==类里面每个用例结束后都会执行teardown_method==
====整个测试类结束后只执行一次teardown_class====
===每个函数级别用例开始前都执行setup_function===
E===每个函数级别用例结束后都执行teardown_function====
=====整个.py模块结束后只执行一次:关闭浏览器=====

==================================== ERRORS ====================================
_________________________ ERROR at setup of test_four __________________________
file /home/ubuntu/MySpace/Python/pytest/setup_teardown.py, line 48
  def test_four(self):
E       fixture ‘self‘ not found
>       available fixtures: _Module__pytest_setup_function, _Module__pytest_setup_module, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/setup_teardown.py:48
=========================== short test summary info ============================
ERROR setup_teardown.py::test_four
3 passed, 1 error in 0.05s
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 4 items                                                              

setup_teardown.py ...E                                                   [100%]

==================================== ERRORS ====================================
_________________________ ERROR at setup of test_four __________________________
file /home/ubuntu/MySpace/Python/pytest/setup_teardown.py, line 48
  def test_four(self):
E       fixture ‘self‘ not found
>       available fixtures: _Module__pytest_setup_function, _Module__pytest_setup_module, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/setup_teardown.py:48
---------------------------- Captured stdout setup -----------------------------
===每个函数级别用例开始前都执行setup_function===
=========================== short test summary info ============================
ERROR setup_teardown.py::test_four
========================== 3 passed, 1 error in 0.04s ==========================

fixture

如果有以下场景:用例 1 需要先登录,用例 2 不需要登录,用例 3 需要先登录。很显然无法用 setup 和 teardown 来实现了

fixture可以让我们自定义测试用例的前置条件

fixture基础

fixture的优势

fixture参数列表

@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
def test():
    print("fixture初始化的参数列表")

调用 fixture

方法一:将fixture名称作为测试用例函数的输入参数

import pytest

@pytest.fixture
def login():
    print("请输入帐号和密码")
    
def test_s1(login):
    print("用例1,登录后进行的,操作 111")
    
"""不穿login"""
def test_s2():
    print("用例2,不需要登录,操作 222")
    
    
!pytest fixture.py::test_s1
!pytest fixture.py::test_s2
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

fixture.py .                                                             [100%]

============================== 1 passed in 0.01s ===============================
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

fixture.py .                                                             [100%]

============================== 1 passed in 0.01s ===============================

方法二:测试用例加上装饰器:@pytest.mark.usefixtures(fixture_name)

import pytest

@pytest.fixture
def login():
    print("请输入帐号和密码")

@pytest.fixture
def login2():
    print("please输入账号,密码先登录")


@pytest.mark.usefixtures("login2", "login")
def test_s11():
    print("用例 11:登录之后其它动作 111")

!pytest fixture.py::test_s11
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

fixture.py .                                                             [100%]

============================== 1 passed in 0.01s ===============================

方法三:fixture设置autouse=True

@pytest.fixture(autouse=True)
def login3():
    print("====auto===")


"""不是test开头,加了装饰器也不会执行fixture"""
@pytest.mark.usefixtures("login2")
def test_loginss():
    print(123)
    
    
!pytest fixture.py::test_loginss
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

fixture.py .                                                             [100%]

============================== 1 passed in 0.01s ===============================

fixture实例化顺序

import pytest

order = []

@pytest.fixture(scope="session")
def s1():
    order.append("s1")


@pytest.fixture(scope="module")
def m1():
    order.append("m1")
    
@pytest.fixture
def f1(f3, a1):
    # 先实例化f3, 再实例化a1, 最后实例化f1
    order.append("f1")
    assert f3 == 123


@pytest.fixture
def f3():
    order.append("f3")
    a = 123
    yield a


@pytest.fixture
def a1():
    order.append("a1")


@pytest.fixture
def f2():
    order.append("f2")


def test_order(f1, m1, f2, s1):
    # m1、s1在f1后,但因为scope范围大,所以会优先实例化
    assert order == ["s1", "m1", "f3", "a1", "f1", "f2"]
    
    
!pytest fixture_order.py::test_order
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

fixture_order.py .                                                       [100%]

============================== 1 passed in 0.01s ===============================

fixture注意点

添加了 @pytest.fixture ,如果fixture还想依赖其他fixture,需要用函数传参的方式,不能用 @pytest.mark.usefixtures() 的方式,否则会不生效

import pytest

@pytest.fixture(scope="session")
def open():
    print("===打开浏览器===")

@pytest.fixture
# @pytest.mark.usefixtures("open") 不可取!!!不生效!!!
def test_login(open):
    # 方法级别前置操作setup
    print(f"输入账号,密码先登录{open}")
    
    
!pytest fixture_attention.py::test_login
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 0 items                                                              

============================ no tests ran in 0.01s =============================
ERROR: not found: /home/ubuntu/MySpace/Python/pytest/fixture_attention.py::test_login
(no name ‘/home/ubuntu/MySpace/Python/pytest/fixture_attention.py::test_login‘ in any of [<Module fixture_attention.py>])


fixture实现前置和后置

fixture之yield实现teardown

用fixture实现teardown并不是一个独立的函数,而是用 yield 关键字来开启teardown操作

import pytest


@pytest.fixture(scope="session")
def open():
    # 会话前置操作setup
    print("===打开浏览器===")
    test = "测试变量是否返回"
    yield test
    # 会话后置操作teardown
    print("==关闭浏览器==")


@pytest.fixture
def login(open):
    # 方法级别前置操作setup
    print(f"输入账号,密码先登录{open}")
    name = "==我是账号=="
    pwd = "==我是密码=="
    age = "==我是年龄=="
    # 返回变量
    yield name, pwd, age
    # 方法级别后置操作teardown
    print("登录成功")


def test_s1(login):
    print("==用例1==")
    # 返回的是一个元组
    print(login)
    # 分别赋值给不同变量
    name, pwd, age = login
    print(name, pwd, age)
    assert "账号" in name
    assert "密码" in pwd
    assert "年龄" in age


def test_s2(login):
    print("==用例2==")
    print(login)
    
    
!pytest fixture_teardown.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

fixture_teardown.py ..                                                   [100%]

============================== 2 passed in 0.01s ===============================

with和yeild

import pytest

@pytest.fixture(scope="module")
def smtp_connection():
    with smtp.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection  # provide the fixture value
        
        
!pytest fixture_yeild_with.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 0 items                                                              

============================ no tests ran in 0.00s =============================

addfinalizer函数

@pytest.fixture(scope="module")
def test_addfinalizer(request):
    # 前置操作setup
    print("==再次打开浏览器==")
    test = "test_addfinalizer"

    def fin():
        # 后置操作teardown
        print("==再次关闭浏览器==")

    request.addfinalizer(fin)
    # 返回前置操作的变量
    return test


def test_anthor(test_addfinalizer):
    print("==最新用例==", test_addfinalizer)

conftest.py

!tree ./conftest/

!pytest ./conftest/run.py
./conftest/
├── confest.py
├── __pycache__
│?? └── test_1.cpython-36-pytest-5.4.1.pyc
├── run.py
└── test_1.py

1 directory, 4 files
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 0 items                                                              

============================ no tests ran in 0.00s =============================

fixture传参request

一个参数

import pytest


@pytest.fixture()
def login(request):
    name = request.param
    print(f"== 账号是:{name} ==")
    return name


data = ["pyy1", "polo"]
ids = [f"login_test_name is:{name}" for name in data]

"""
(1)添加  indirect=True  参数是为了把 login 当成一个函数去执行,而不是一个参数,并且将data当做参数传入函数
(2)def test_name(login) ,这里的login是获取fixture返回的值
"""
@pytest.mark.parametrize("login", data, ids=ids, indirect=True)
def test_name(login):
    print(f" 测试用例的登录账号是:{login} ")
    
    
!pytest -s fixture/fixture_request_one.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

fixture/fixture_request_one.py == 账号是:pyy1 ==
 测试用例的登录账号是:pyy1 
.== 账号是:polo ==
 测试用例的登录账号是:polo 
.

============================== 2 passed in 0.01s ===============================

多个参数

需要传多个参数,需要通过字典去传

"""多个参数"""
import pytest


@pytest.fixture()
def login(request):
    param = request.param
    print(f"账号是:{param[‘username‘]}, 密码是:{param[‘passwd‘]}")
    return param

data = [
    {"username": "crisimple1", "passwd": "123456"},
    {"username": "crisimple2", "passwd": "654321"},
]

@pytest.mark.parametrize("login", data, indirect=True)
def test_login(login):
    print(f"账号是:{login[‘username‘]},密码是:{login[‘passwd‘]}")
    
    
!pytest -v -s fixture/fixture_request_more.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

fixture/fixture_request_more.py::test_login[login0] 账号是:crisimple1, 密码是:123456
账号是:crisimple1,密码是:123456
PASSED
fixture/fixture_request_more.py::test_login[login1] 账号是:crisimple2, 密码是:654321
账号是:crisimple2,密码是:654321
PASSED

============================== 2 passed in 0.01s ===============================

多个fixture(只加一个装饰器)

"""多个fixture,只加一个装饰器"""
import pytest

@pytest.fixture(scope="function")
def input_user(request):
    user = request.param
    print("登录账号为:%s" % user)
    return user

@pytest.fixture(scope="function")
def input_pwd(request):
    pwd = request.param
    print("登录密码为:%s" % pwd)
    return pwd

data = [
    ("name1", "pwd1"),
    ("name2", "pwd2")
]

@pytest.mark.parametrize("input_user, input_pwd", data, indirect=True)
def test_more_fixture(input_user, input_pwd):
    print("fixture返回的内容:", input_user, input_pwd)
    

!pytest -v -s fixture/fixture_one_fixture.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 2 items                                                              

fixture/fixture_one_fixture.py::test_more_fixture[name1-pwd1] 登录账号为:name1
登录密码为:pwd1
fixture返回的内容: name1 pwd1
PASSED
fixture/fixture_one_fixture.py::test_more_fixture[name2-pwd2] 登录账号为:name2
登录密码为:pwd2
fixture返回的内容: name2 pwd2
PASSED

============================== 2 passed in 0.01s ===============================

多个fixture(叠加装饰器)

"""多个fixture"""
import pytest

@pytest.fixture(scope="function")
def input_user(request):
    user = request.param
    print("登录账号为:%s" % user)
    return user

@pytest.fixture(scope="function")
def input_pwd(request):
    pwd = request.param
    print("登录密码为:%s" % pwd)
    return pwd

name = ["name1", "name2"]
passwd = ["pwd1", "pwd2"]

@pytest.mark.parametrize("input_user", name, indirect=True)
@pytest.mark.parametrize("input_pwd", passwd, indirect=True)
def test_more_fixture(input_user, input_pwd):
    print("fixture返回的内容:", input_user, input_pwd)
    
    
!pytest  -v -s fixture/fixture_more_fixture.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 4 items                                                              

fixture/fixture_more_fixture.py::test_more_fixture[pwd1-name1] 登录账号为:name1
登录密码为:pwd1
fixture返回的内容: name1 pwd1
PASSED
fixture/fixture_more_fixture.py::test_more_fixture[pwd1-name2] 登录账号为:name2
登录密码为:pwd1
fixture返回的内容: name2 pwd1
PASSED
fixture/fixture_more_fixture.py::test_more_fixture[pwd2-name1] 登录账号为:name1
登录密码为:pwd2
fixture返回的内容: name1 pwd2
PASSED
fixture/fixture_more_fixture.py::test_more_fixture[pwd2-name2] 登录账号为:name2
登录密码为:pwd2
fixture返回的内容: name2 pwd2
PASSED

============================== 4 passed in 0.02s ===============================

skip、skipif跳过用例

pytest.mark.skip

跳过执行测试用例,有可选参数 reason:跳过的原因,会在执行结果中打印

import pytest


@pytest.fixture(autouse=True)
def login():
    print("====登录====")


def test_case01():
    print("我是测试用例11111")


@pytest.mark.skip(reason="不执行该用例!!因为没写好!!")
def test_case02():
    print("我是测试用例22222")


class Test1:

    def test_1(self):
        print("%% 我是类测试用例1111 %%")

    @pytest.mark.skip(reason="不想执行")
    def test_2(self):
        print("%% 我是类测试用例2222 %%")


@pytest.mark.skip(reason="类也可以跳过不执行")
class TestSkip:
    def test_1(self):
        print("%% 不会执行 %%")

!pytest skip_skipif.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 5 items                                                              

skip_skipif.py .s.ss                                                     [100%]

========================= 2 passed, 3 skipped in 0.02s =========================

pytest.mark()

def test_function():
    n = 1
    while True:
        print(f"这是我第{n}条用例")
        n += 1
        if n == 5:
            pytest.skip("我跑五次了不跑了")
            
test_function()
这是我第1条用例
这是我第2条用例
这是我第3条用例
这是我第4条用例



---------------------------------------------------------------------------

Skipped                                   Traceback (most recent call last)

<ipython-input-76-4408d6b29e56> in <module>
      7             pytest.skip("我跑五次了不跑了")
      8 
----> 9 test_function()


<ipython-input-76-4408d6b29e56> in test_function()
      5         n += 1
      6         if n == 5:
----> 7             pytest.skip("我跑五次了不跑了")
      8 
      9 test_function()


~/.local/lib/python3.6/site-packages/_pytest/outcomes.py in skip(msg, allow_module_level)
    143     """
    144     __tracebackhide__ = True
--> 145     raise Skipped(msg=msg, allow_module_level=allow_module_level)
    146 
    147 


Skipped: 我跑五次了不跑了

pytest.skip(msg="", allow_module_level=False)

当 allow_module_level=True,可以设置在模块级别跳过整个模块

import sys
import pytest

if sys.platform.startswith("win"):
    pytest.skip("skipping windows-only tests", allow_module_level=True)


@pytest.fixture(autouse=True)
def login():
    print("====登录====")


def test_case01():
    print("我是测试用例11111")
    

!pytest skip_allow_module_level.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

skip_allow_module_level.py .                                             [100%]

============================== 1 passed in 0.01s ===============================

pytest.mark.skipif(condition, reason="")

@pytest.mark.skipif(sys.platform == ‘linux‘, reason="does not run on windows")
class TestSkipIf(object):
    def test_function(self):
        print("不能在window上运行")
        


!pytest skip_makr_skipif.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 1 item                                                               

skip_makr_skipif.py .                                                    [100%]

============================== 1 passed in 0.01s ===============================

跳过标记

# 标记
skipmark = pytest.mark.skip(reason="不能在window上运行=====")
skipifmark = pytest.mark.skipif(sys.platform == ‘win32‘, reason="不能在window上运行啦啦啦=====")


@skipmark
class TestSkip_Mark(object):

    @skipifmark
    def test_function(self):
        print("测试标记")

    def test_def(self):
        print("测试标记")


@skipmark
def test_skip():
    print("测试标记")
    

!pytest skip_variable.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
collected 3 items                                                              

skip_variable.py sss                                                     [100%]

============================== 3 skipped in 0.01s ==============================

pytest.importtoskip(modname:str, minversion:Optional[str]=None,reason:Optional[str]=None)

pexpect = pytest.importorskip("pexpect", minversion="0.3")


@pexpect
def test_import():
    print("test")
    

!pytest skip_importtoskip.py
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-88-05d178e742c2> in <module>
      2 
      3 
----> 4 @pexpect
      5 def test_import():
      6     print("test")


TypeError: ‘module‘ object is not callable

自定义标记mark

pytest 可以支持自定义标记,自定义标记可以把一个 web 项目划分多个模块,然后指定模块名称执行

mark的基本用法

import pytest


@pytest.mark.toutiao
def test_toutiao():
    print("测试头条")
    
@pytest.mark.weibo
def test_weibo():
    print("测试微博")
    
@pytest.mark.toutiao
def test_toutiao1():
    print("再次测试头条")
    
@pytest.mark.xinlang
class TestClass:
    def test_method(self):
        print("测试新浪")
        
"""没有标记测试"""
def test_nomark():
    print("没有标记测试")


!pytest -s -m weibo mark/mark.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 5 items / 4 deselected / 1 selected                                  

mark/mark.py 测试微博
.

=============================== warnings summary ===============================
mark/mark.py:4
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:4: PytestUnknownMarkWarning: Unknown pytest.mark.toutiao - 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.mark.toutiao

mark/mark.py:12
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:12: PytestUnknownMarkWarning: Unknown pytest.mark.toutiao - 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.mark.toutiao

mark/mark.py:16
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:16: PytestUnknownMarkWarning: Unknown pytest.mark.xinlang - 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.mark.xinlang

-- Docs: https://docs.pytest.org/en/latest/warnings.html
================= 1 passed, 4 deselected, 3 warnings in 0.02s ==================
"""
问题:上面的执行结果是有 warning 的
解决方案:在 同级目录 下添加 pytest.ini 配置文件,加上自定义mark
注意:pytest.ini需要和运行的测试用例同一个目录,或在根目录下作用于全局
"""

!pytest -s -m weibo mark/mark.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 5 items / 4 deselected / 1 selected                                  

mark/mark.py 测试微博
.

=============================== warnings summary ===============================
mark/mark.py:4
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:4: PytestUnknownMarkWarning: Unknown pytest.mark.toutiao - 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.mark.toutiao

mark/mark.py:12
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:12: PytestUnknownMarkWarning: Unknown pytest.mark.toutiao - 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.mark.toutiao

mark/mark.py:16
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:16: PytestUnknownMarkWarning: Unknown pytest.mark.xinlang - 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.mark.xinlang

-- Docs: https://docs.pytest.org/en/latest/warnings.html
================= 1 passed, 4 deselected, 3 warnings in 0.01s ==================
"""如果不想执行标记 weibo 的用例,取反即可"""
!pytest -s -m "no weibo" mark/mark.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 5 items                                                              
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/mark/legacy.py", line 86, in matchmark
INTERNALERROR>     return eval(markexpr, {}, MarkMapping.from_item(colitem))
INTERNALERROR>   File "<string>", line 1
INTERNALERROR>     no weibo
INTERNALERROR>            ^
INTERNALERROR> SyntaxError: unexpected EOF while parsing
INTERNALERROR> 
INTERNALERROR> During handling of the above exception, another exception occurred:
INTERNALERROR> 
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/main.py", line 191, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/main.py", line 246, in _main
INTERNALERROR>     config.hook.pytest_collection(session=session)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/manager.py", line 87, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/main.py", line 257, in pytest_collection
INTERNALERROR>     return session.perform_collect()
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/main.py", line 455, in perform_collect
INTERNALERROR>     session=self, config=self.config, items=items
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/manager.py", line 87, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/mark/__init__.py", line 151, in pytest_collection_modifyitems
INTERNALERROR>     deselect_by_mark(items, config)
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/mark/__init__.py", line 139, in deselect_by_mark
INTERNALERROR>     if matchmark(item, matchexpr):
INTERNALERROR>   File "/home/ubuntu/.local/lib/python3.6/site-packages/_pytest/mark/legacy.py", line 88, in matchmark
INTERNALERROR>     raise SyntaxError(str(e) + "\nMarker expression must be valid Python!")
INTERNALERROR>   File "<string>", line None
INTERNALERROR> SyntaxError: unexpected EOF while parsing (<string>, line 1)
INTERNALERROR> Marker expression must be valid Python!

============================= 3 warnings in 0.02s ==============================
"""执行多个自定义标记的用例"""
!pytest -s -m "toutiao or weibo" mark/mark.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 5 items / 2 deselected / 3 selected                                  

mark/mark.py 测试头条
.测试微博
.再次测试头条
.

=============================== warnings summary ===============================
mark/mark.py:4
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:4: PytestUnknownMarkWarning: Unknown pytest.mark.toutiao - 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.mark.toutiao

mark/mark.py:12
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:12: PytestUnknownMarkWarning: Unknown pytest.mark.toutiao - 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.mark.toutiao

mark/mark.py:16
  /home/ubuntu/MySpace/Python/pytest/mark/mark.py:16: PytestUnknownMarkWarning: Unknown pytest.mark.xinlang - 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.mark.xinlang

-- Docs: https://docs.pytest.org/en/latest/warnings.html
================= 3 passed, 2 deselected, 3 warnings in 0.02s ==================

参数化@pytest.mark.parametrize

def add_sample(a, b):
    return a + b

def test_1():
    assert 3 + 5 == 9


def test_2():
    assert 2 + 4 == 6


def test_3():
    assert 6 * 9 == 42

实际应用场景:实际Web UI自动化中的开发场景,比如是一个登录框

"""将上面重复的代码参数化"""
import pytest

@pytest.mark.parametrize("test_input, excepted", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, excepted):
    print(f"测试数据{test_input}, 期望结果{excepted}")
    assert eval(test_input) == excepted
    
    
!pytest mark/mark_parametrize.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 3 items                                                              

mark/mark_parametrize.py ..F                                             [100%]

=================================== FAILURES ===================================
______________________________ test_eval[6*9-42] _______________________________

test_input = ‘6*9‘, excepted = 42

    @pytest.mark.parametrize("test_input, excepted", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
    def test_eval(test_input, excepted):
        print(f"测试数据{test_input}, 期望结果{excepted}")
>       assert eval(test_input) == excepted
E       AssertionError: assert 54 == 42
E        +  where 54 = eval(‘6*9‘)

mark/mark_parametrize.py:6: AssertionError
----------------------------- Captured stdout call -----------------------------
测试数据6*9, 期望结果42
=========================== short test summary info ============================
FAILED mark/mark_parametrize.py::test_eval[6*9-42] - AssertionError: assert 5...
========================= 1 failed, 2 passed in 0.11s ==========================

源码解读

def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None): 
    pass
@pytest.mark.parametrize(["name", "pwd"], [("yy1", "123"), ("yy2", "123")])  # 错的
@pytest.mark.parametrize(("name", "pwd"), [("yy1", "123"), ("yy2", "123")])  # 错的
@pytest.mark.parametrize("name,pwd", [("yy1", "123"), ("yy2", "123")])

装饰测试类

当装饰器 @pytest.mark.parametrize 装饰测试类时,会将数据集合传递给类的所有测试用例方法

import pytest

data_1 = [1, 2, 3]

@pytest.mark.parametrize(‘a, b, expect‘, data_1)
class TestParametrize:
    def test_parametrize_1(self, a, b, expect):
        print(‘\n测试函数11111 测试数据为\n{}-{}‘.format(a, b))
        assert a + b == expect

    def test_parametrize_2(self, a, b, expect):
        print(‘\n测试函数22222 测试数据为\n{}-{}‘.format(a, b))
        assert a + b == expect
        
        
!pytest -v mark/mark_class_parametrize.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 0 items / 1 error                                                    

==================================== ERRORS ====================================
__________________ ERROR collecting mark_class_parametrize.py __________________
../../../.local/lib/python3.6/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
../../../.local/lib/python3.6/site-packages/pluggy/manager.py:93: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
../../../.local/lib/python3.6/site-packages/pluggy/manager.py:87: in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
../../../.local/lib/python3.6/site-packages/_pytest/python.py:248: in pytest_pycollect_makeitem
    res = list(collector._genfunctions(name, obj))
../../../.local/lib/python3.6/site-packages/_pytest/python.py:415: in _genfunctions
    self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
../../../.local/lib/python3.6/site-packages/pluggy/hooks.py:324: in call_extra
    return self(**kwargs)
../../../.local/lib/python3.6/site-packages/pluggy/hooks.py:286: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
../../../.local/lib/python3.6/site-packages/pluggy/manager.py:93: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
../../../.local/lib/python3.6/site-packages/pluggy/manager.py:87: in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
../../../.local/lib/python3.6/site-packages/_pytest/python.py:139: in pytest_generate_tests
    metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker)  # type: ignore[misc] # noqa: F821
../../../.local/lib/python3.6/site-packages/_pytest/python.py:922: in parametrize
    function_definition=self.definition,
../../../.local/lib/python3.6/site-packages/_pytest/mark/structures.py:114: in _for_parametrize
    if len(param.values) != len(argnames):
E   TypeError: object of type ‘int‘ has no len()
=========================== short test summary info ============================
ERROR mark/mark_class_parametrize.py::TestParametrize - TypeError: object of ...
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
=============================== 1 error in 0.37s ===============================

"笛卡尔积"-多个参数化装饰器

"""笛卡尔积,组合数据"""
import pytest

data_1 = [1, 2, 3]
data_2 = [‘a‘, ‘b‘]


@pytest.mark.parametrize(‘a‘, data_1)
@pytest.mark.parametrize(‘b‘, data_2)
def test_parametrize_1(a, b):
    print(f‘笛卡尔积 测试数据为 : {a},{b}‘)
    
    
!pytest -v mark/mark_many_parametrize.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 6 items                                                              

mark/mark_many_parametrize.py::test_parametrize_1[a-1] PASSED            [ 16%]
mark/mark_many_parametrize.py::test_parametrize_1[a-2] PASSED            [ 33%]
mark/mark_many_parametrize.py::test_parametrize_1[a-3] PASSED            [ 50%]
mark/mark_many_parametrize.py::test_parametrize_1[b-1] PASSED            [ 66%]
mark/mark_many_parametrize.py::test_parametrize_1[b-2] PASSED            [ 83%]
mark/mark_many_parametrize.py::test_parametrize_1[b-3] PASSED            [100%]

============================== 6 passed in 0.03s ===============================

参数化-传入字典数据

"""字典"""
import pytest

data_1 = (
    {
        ‘user‘: 1,
        ‘pwd‘: 2
    },
    {
        ‘user‘: 3,
        ‘pwd‘: 4
    }
)


@pytest.mark.parametrize(‘dic‘, data_1)
def test_parametrize_1(dic):
    print(f‘测试数据为\n{dic}‘)
    print(f‘user:{dic["user"]},pwd{dic["pwd"]}‘)
    
    
!pytest -s mark/mark_dict_parametrize.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
collected 2 items                                                              

mark/mark_dict_parametrize.py 测试数据为
{‘user‘: 1, ‘pwd‘: 2}
user:1,pwd2
.测试数据为
{‘user‘: 3, ‘pwd‘: 4}
user:3,pwd4
.

============================== 2 passed in 0.01s ===============================

参数化-标记数据

"""标记参数化"""
import pytest

@pytest.mark.parametrize("test_input,expected", [
    ("3+5", 8),
    ("2+4", 6),
    pytest.param("6 * 9", 42, marks=pytest.mark.xfail),
    pytest.param("6*6", 42, marks=pytest.mark.skip)
])

def test_mark(test_input, expected):
    assert eval(test_input) == expected
    
    
!pytest -v mark/mark_mark_data.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest/mark, inifile: pytest.ini
plugins: rerunfailures-9.0, repeat-0.8.0
collected 4 items                                                              

mark/mark_mark_data.py::test_mark[3+5-8] PASSED                          [ 25%]
mark/mark_mark_data.py::test_mark[2+4-6] PASSED                          [ 50%]
mark/mark_mark_data.py::test_mark[6 * 9-42] XFAIL                        [ 75%]
mark/mark_mark_data.py::test_mark[6*6-42] SKIPPED                        [100%]

=================== 2 passed, 1 skipped, 1 xfailed in 0.04s ====================
"""增加可读性"""
import pytest

data_1 = [
    (1, 2, 3),
    (4, 5, 9)
]

ids = ["a:{} + b:{} = expect:{}".format(a, b, expect) for a, b, expect in data_1]

@pytest.mark.parametrize(‘a, b, expect‘, data_1, ids=ids)
class TestParametrize(object):

    def test_parametrize_1(self, a, b, expect):
        print(‘测试函数1测试数据为{}-{}‘.format(a, b))
        assert a + b == expect

    def test_parametrize_2(self, a, b, expect):
        print(‘测试函数2数据为{}-{}‘.format(a, b))
        assert a + b == expect
        

!pytest -v mark/mark_read.py

参数化-增加可读性

多少组数据,就要有多少个id,然后组成一个id的列表

作用:主要是为了更加清晰看到用例的含义

pytest.ini

pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行

非test文件

pytest里面有些文件是非test文件

"""查看pytest.ini的配置选项"""
!pytest --help
usage: pytest [options] [file_or_dir] [file_or_dir] [...]

positional arguments:
  file_or_dir

general:
  -k EXPRESSION         only run tests which match the given substring
                        expression. An expression is a python evaluatable
                        expression where all names are substring-matched against
                        test names and their parent classes. Example: -k
                        ‘test_method or test_other‘ matches all test functions
                        and classes whose name contains ‘test_method‘ or
                        ‘test_other‘, while -k ‘not test_method‘ matches those
                        that don‘t contain ‘test_method‘ in their names. -k ‘not
                        test_method and not test_other‘ will eliminate the
                        matches. Additionally keywords are matched to classes
                        and functions containing extra names in their
                        ‘extra_keyword_matches‘ set, as well as functions which
                        have names assigned directly to them. The matching is
                        case-insensitive.
  -m MARKEXPR           only run tests matching given mark expression. example:
                        -m ‘mark1 and not mark2‘.
  --markers             show markers (builtin, plugin and per-project ones).
  -x, --exitfirst       exit instantly on first error or failed test.
  --maxfail=num         exit after first num failures or errors.
  --strict-markers, --strict
                        markers not registered in the `markers` section of the
                        configuration file raise errors.
  -c file               load configuration from `file` instead of trying to
                        locate one of the implicit configuration files.
  --continue-on-collection-errors
                        Force test execution even if collection errors occur.
  --rootdir=ROOTDIR     Define root directory for tests. Can be relative path:
                        ‘root_dir‘, ‘./root_dir‘, ‘root_dir/another_dir/‘;
                        absolute path: ‘/home/user/root_dir‘; path with
                        variables: ‘$HOME/root_dir‘.
  --fixtures, --funcargs
                        show available fixtures, sorted by plugin appearance
                        (fixtures with leading ‘_‘ are only shown with ‘-v‘)
  --fixtures-per-test   show fixtures per test
  --import-mode={prepend,append}
                        prepend/append to sys.path when importing test modules,
                        default is to prepend.
  --pdb                 start the interactive Python debugger on errors or
                        KeyboardInterrupt.
  --pdbcls=modulename:classname
                        start a custom interactive Python debugger on errors.
                        For example:
                        --pdbcls=IPython.terminal.debugger:TerminalPdb
  --trace               Immediately break when running each test.
  --capture=method      per-test capturing method: one of fd|sys|no|tee-sys.
  -s                    shortcut for --capture=no.
  --runxfail            report the results of xfail tests as if they were not
                        marked
  --lf, --last-failed   rerun only the tests that failed at the last run (or all
                        if none failed)
  --ff, --failed-first  run all tests but run the last failures first. This may
                        re-order tests and thus lead to repeated fixture
                        setup/teardown
  --nf, --new-first     run tests from new files first, then the rest of the
                        tests sorted by file mtime
  --cache-show=[CACHESHOW]
                        show cache contents, don‘t perform collection or tests.
                        Optional argument: glob (default: ‘*‘).
  --cache-clear         remove all cache contents at start of test run.
  --lfnf={all,none}, --last-failed-no-failures={all,none}
                        which tests to run with no previously (known) failures.
  --sw, --stepwise      exit on test failure and continue from last failing test
                        next time
  --stepwise-skip       ignore the first failing test but stop on the next
                        failing test

reporting:
  --durations=N         show N slowest setup/test durations (N=0 for all).
  -v, --verbose         increase verbosity.
  -q, --quiet           decrease verbosity.
  --verbosity=VERBOSE   set verbosity. Default is 0.
  -r chars              show extra test summary info as specified by chars:
                        (f)ailed, (E)rror, (s)kipped, (x)failed, (X)passed,
                        (p)assed, (P)assed with output, (a)ll except passed
                        (p/P), or (A)ll. (w)arnings are enabled by default (see
                        --disable-warnings), ‘N‘ can be used to reset the list.
                        (default: ‘fE‘).
  --disable-warnings, --disable-pytest-warnings
                        disable warnings summary
  -l, --showlocals      show locals in tracebacks (disabled by default).
  --tb=style            traceback print mode (auto/long/short/line/native/no).
  --show-capture={no,stdout,stderr,log,all}
                        Controls how captured stdout/stderr/log is shown on
                        failed tests. Default is ‘all‘.
  --full-trace          don‘t cut any tracebacks (default is to cut).
  --color=color         color terminal output (yes/no/auto).
  --pastebin=mode       send failed|all info to bpaste.net pastebin service.
  --junit-xml=path      create junit-xml style report file at given path.
  --junit-prefix=str    prepend prefix to classnames in junit-xml output
  --result-log=path     DEPRECATED path for machine-readable result log.

collection:
  --collect-only, --co  only collect tests, don‘t execute them.
  --pyargs              try to interpret all arguments as python packages.
  --ignore=path         ignore path during collection (multi-allowed).
  --ignore-glob=path    ignore path pattern during collection (multi-allowed).
  --deselect=nodeid_prefix
                        deselect item (via node id prefix) during collection
                        (multi-allowed).
  --confcutdir=dir      only load conftest.py‘s relative to specified dir.
  --noconftest          Don‘t load any conftest.py files.
  --keep-duplicates     Keep duplicate tests.
  --collect-in-virtualenv
                        Don‘t ignore tests in a local virtualenv directory
  --doctest-modules     run doctests in all .py modules
  --doctest-report={none,cdiff,ndiff,udiff,only_first_failure}
                        choose another output format for diffs on doctest
                        failure
  --doctest-glob=pat    doctests file matching pattern, default: test*.txt
  --doctest-ignore-import-errors
                        ignore doctest ImportErrors
  --doctest-continue-on-failure
                        for a given doctest, continue to run after the first
                        failure

test session debugging and configuration:
  --basetemp=dir        base temporary directory for this test run.(warning:
                        this directory is removed if it exists)
  -V, --version         display pytest version and information about plugins.
  -h, --help            show help message and configuration info
  -p name               early-load given plugin module name or entry point
                        (multi-allowed). To avoid loading of plugins, use the
                        `no:` prefix, e.g. `no:doctest`.
  --trace-config        trace considerations of conftest.py files.
  --debug               store internal tracing debug information in
                        ‘pytestdebug.log‘.
  -o OVERRIDE_INI, --override-ini=OVERRIDE_INI
                        override ini option with "option=value" style, e.g. `-o
                        xfail_strict=True -o cache_dir=cache`.
  --assert=MODE         Control assertion debugging tools. ‘plain‘ performs no
                        assertion debugging. ‘rewrite‘ (the default) rewrites
                        assert statements in test modules on import to provide
                        assert expression information.
  --setup-only          only setup fixtures, do not execute tests.
  --setup-show          show setup of fixtures while executing tests.
  --setup-plan          show what fixtures and tests would be executed but don‘t
                        execute anything.

pytest-warnings:
  -W PYTHONWARNINGS, --pythonwarnings=PYTHONWARNINGS
                        set which warnings to report, see -W option of python
                        itself.

logging:
  --no-print-logs       disable printing caught logs on failed tests.
  --log-level=LEVEL     level of messages to catch/display. Not set by default,
                        so it depends on the root/parent log handler‘s effective
                        level, where it is "WARNING" by default.
  --log-format=LOG_FORMAT
                        log format as used by the logging module.
  --log-date-format=LOG_DATE_FORMAT
                        log date format as used by the logging module.
  --log-cli-level=LOG_CLI_LEVEL
                        cli logging level.
  --log-cli-format=LOG_CLI_FORMAT
                        log format as used by the logging module.
  --log-cli-date-format=LOG_CLI_DATE_FORMAT
                        log date format as used by the logging module.
  --log-file=LOG_FILE   path to a file when logging will be written to.
  --log-file-level=LOG_FILE_LEVEL
                        log file logging level.
  --log-file-format=LOG_FILE_FORMAT
                        log format as used by the logging module.
  --log-file-date-format=LOG_FILE_DATE_FORMAT
                        log date format as used by the logging module.
  --log-auto-indent=LOG_AUTO_INDENT
                        Auto-indent multiline messages passed to the logging
                        module. Accepts true|on, false|off or an integer.

re-run failing tests to eliminate flaky failures:
  --reruns=RERUNS       number of times to re-run failed tests. defaults to 0.
  --reruns-delay=RERUNS_DELAY
                        add time (seconds) delay between reruns.

custom options:
  --count=COUNT         Number of times to repeat each test
  --repeat-scope={function,class,module,session}
                        Scope for repeating tests

[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:

  markers (linelist):   markers for test functions
  empty_parameter_set_mark (string):
                        default marker for empty parametersets
  norecursedirs (args): directory patterns to avoid for recursion
  testpaths (args):     directories to search for tests when no files or
                        directories are given in the command line.
  usefixtures (args):   list of default fixtures to be used with this project
  python_files (args):  glob-style file patterns for Python test module
                        discovery
  python_classes (args):
                        prefixes or glob names for Python test class discovery
  python_functions (args):
                        prefixes or glob names for Python test function and
                        method discovery
  disable_test_id_escaping_and_forfeit_all_rights_to_community_support (bool):
                        disable string escape non-ascii characters, might cause
                        unwanted side effects(use at your own risk)
  console_output_style (string):
                        console output: "classic", or with additional progress
                        information ("progress" (percentage) | "count").
  xfail_strict (bool):  default for the strict parameter of xfail markers when
                        not given explicitly (default: False)
  enable_assertion_pass_hook (bool):
                        Enables the pytest_assertion_pass hook.Make sure to
                        delete any previously generated pyc cache files.
  junit_suite_name (string):
                        Test suite name for JUnit report
  junit_logging (string):
                        Write captured log messages to JUnit report: one of
                        no|log|system-out|system-err|out-err|all
  junit_log_passing_tests (bool):
                        Capture log information for passing tests to JUnit
                        report:
  junit_duration_report (string):
                        Duration time to report: one of total|call
  junit_family (string):
                        Emit XML for schema: one of legacy|xunit1|xunit2
  doctest_optionflags (args):
                        option flags for doctests
  doctest_encoding (string):
                        encoding used for doctest files
  cache_dir (string):   cache directory path.
  filterwarnings (linelist):
                        Each line specifies a pattern for
                        warnings.filterwarnings. Processed after
                        -W/--pythonwarnings.
  log_print (bool):     default value for --no-print-logs
  log_level (string):   default value for --log-level
  log_format (string):  default value for --log-format
  log_date_format (string):
                        default value for --log-date-format
  log_cli (bool):       enable log display during test run (also known as "live
                        logging").
  log_cli_level (string):
                        default value for --log-cli-level
  log_cli_format (string):
                        default value for --log-cli-format
  log_cli_date_format (string):
                        default value for --log-cli-date-format
  log_file (string):    default value for --log-file
  log_file_level (string):
                        default value for --log-file-level
  log_file_format (string):
                        default value for --log-file-format
  log_file_date_format (string):
                        default value for --log-file-date-format
  log_auto_indent (string):
                        default value for --log-auto-indent
  faulthandler_timeout (string):
                        Dump the traceback of all threads if a test takes more
                        than TIMEOUT seconds to finish. Not available on
                        Windows.
  addopts (args):       extra command line options
  minversion (string):  minimally required pytest version

environment variables:
  PYTEST_ADDOPTS           extra command line options
  PYTEST_PLUGINS           comma-separated plugins to load during startup
  PYTEST_DISABLE_PLUGIN_AUTOLOAD set to disable plugin auto-loading
  PYTEST_DEBUG             set to enable debug tracing of pytest‘s internals


to see available markers type: pytest --markers
to see available fixtures type: pytest --fixtures
(shown according to specified file_or_dir or current dir if not specified; fixtures with leading ‘_‘ are only shown with the ‘-v‘ option

pytest.ini常用配置

marks

[pytest]
markers =
    weibo: this is weibo page
    toutiao: toutiao
    xinlang: xinlang

xfail_strict

[pytest]

# mark标记说明
markers =
    weibo: this is weibo page
    toutiao: toutiao
    xinlang: xinlang

xfail_strict = True

addopts

[pytest]

# mark
markers =
    weibo: this is weibo page
    toutiao: toutiao
    xinlang: xinlang

xfail_strict = True

# 命令行参数
addopts = -v --reruns=1 --count=2 --html=reports.html --self-contained-html -n=auto

加了addopts之后,我们在cmd中只需要敲pytest就可以生效了!!

log_cli

log_cli=0的运行结果
log_cli=1的运行结果

很明显,加了log_cli=1之后,可以清晰看到哪个package下的哪个module下的哪个测试用例是否passed还是failed;

所以平时测试代码是否有问题的情况下推荐加!!!但如果拿去批量跑测试用例的话不建议加,谁知道会不会影响运行性能呢?

norecursedirs

[pytest]

norecursedirs = .* build dist CVS _darcs {arch} *.egg venv src resources log report util

更改测试用例收集规则

pytest默认的测试用例收集规则

我们是可以修改或者添加这个用例收集规则的;当然啦,是建议在原有的规则上添加的,如下配置

[pytest]

python_files =     test_*  *_test  test*
python_classes =   Test*   test*
python_functions = test_*  test*

pytest高频插件

失败重跑pytest-rerunfailures

环境配置

环境要求

安装插件

#!pip3 install pytest-rerunfailures -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
Collecting pytest-rerunfailures
  Downloading http://pypi.doubanio.com/packages/25/91/a0d1ff828e6da1915e4972d76ea2b5f9a1b520f078b4197ef93eb8427b65/pytest_rerunfailures-9.0-py3-none-any.whl
Collecting pytest>=5.0 (from pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
    100% |████████████████████████████████| 256kB 23.5MB/s ta 0:00:01
[?25hCollecting setuptools>=40.0 (from pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/a0/df/635cdb901ee4a8a42ec68e480c49f85f4c59e8816effbf57d9e6ee8b3588/setuptools-46.1.3-py3-none-any.whl (582kB)
    100% |████████████████████████████████| 583kB 18.0MB/s ta 0:00:01
[?25hCollecting attrs>=17.4.0 (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting pluggy<1.0,>=0.12 (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting packaging (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting py>=1.5.0 (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
    100% |████████████████████████████████| 92kB 48.5MB/s ta 0:00:01
[?25hCollecting more-itertools>=4.0.0 (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 46.9MB/s ta 0:00:01
[?25hCollecting wcwidth (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
    100% |████████████████████████████████| 71kB 49.6MB/s ta 0:00:01
[?25hCollecting six (from packaging->pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=5.0->pytest-rerunfailures)
  Downloading http://pypi.doubanio.com/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Installing collected packages: attrs, zipp, importlib-metadata, pluggy, pyparsing, six, packaging, py, more-itertools, wcwidth, pytest, setuptools, pytest-rerunfailures
Successfully installed attrs-19.3.0 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 pytest-rerunfailures-9.0 setuptools-46.1.3 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0

配置参数

命令行参数:--reruns n(重新运行次数),--reruns-delay m(等待运行秒数)

装饰器参数:reruns=n(重新运行次数),reruns_delay=m(等待运行秒数)

重新运行所有失败用例

要重新运行所有测试失败,使用 --reruns 命令行选项,并指定要运行测试的最大次数:pytest --rerun 5 -s

运行失败的fixture或setup_class也将重新执行

要在两次重试之间增加延迟时间,使用 --reruns-delay 命令行选项,指定下次测试重新开始之前等待的秒数:pytest --reruns 5 --reruns-delay 10 -s

重新运行指定的测试用例

要将单个测试用例添加flaky装饰器 @pytest.mark.flaky(reruns=5) ,并在测试失败时自动重新运行,需要指定最大重新运行的次数

"""重新运行指定的测试用例"""
import pytest
import random

@pytest.mark.flasky(reruns=5, reruns_delay=2)
def test_example():
    assert random.choice([True, False, False])
    
!pytest -v -s plug/pytest_rerunfailures.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: rerunfailures-9.0
collected 0 items / 1 error                                                    

==================================== ERRORS ====================================
________________ ERROR collecting plug/pytest_rerunfailures.py _________________
import file mismatch:
imported module ‘pytest_rerunfailures‘ has this __file__ attribute:
  /home/ubuntu/.local/lib/python3.6/site-packages/pytest_rerunfailures.py
which is not the same as the test file we want to collect:
  /home/ubuntu/MySpace/Python/pytest/plug/pytest_rerunfailures.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
=========================== short test summary info ============================
ERROR plug/pytest_rerunfailures.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
=============================== 1 error in 0.09s ===============================

注意事项

重复执行pytest-repeat

环境配置

环境要求

插件安装

# !pip3 install pytest-repeat -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
Collecting pytest-repeat
  Downloading http://pypi.doubanio.com/packages/2e/de/c1d69002db74a99b3df0463e95066c03d82d9d2a53be738c140207134e0f/pytest_repeat-0.8.0-py2.py3-none-any.whl
Collecting pytest>=3.6 (from pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
    100% |████████████████████████████████| 256kB 3.8MB/s ta 0:00:011
[?25hCollecting more-itertools>=4.0.0 (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 1.8MB/s ta 0:00:011
[?25hCollecting pluggy<1.0,>=0.12 (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting attrs>=17.4.0 (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting py>=1.5.0 (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
    100% |████████████████████████████████| 92kB 843kB/s ta 0:00:011
[?25hCollecting wcwidth (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting packaging (from pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Collecting six (from packaging->pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest>=3.6->pytest-repeat)
  Downloading http://pypi.doubanio.com/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
    100% |████████████████████████████████| 71kB 40.3MB/s ta 0:00:01
[?25hInstalling collected packages: more-itertools, zipp, importlib-metadata, pluggy, attrs, py, wcwidth, six, pyparsing, packaging, pytest, pytest-repeat
Successfully installed attrs-19.3.0 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 pytest-repeat-0.8.0 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0

重复测试直到失败

import pytest
import random

def test_flag():
    flag = random.choice([True, False])
    print(flag)
    assert flag
    
!pytest -s --count 5 -x plug/test_pytest_repeat.py
ERROR: /home/ubuntu/MySpace/Python/pytest/plug/pytest.ini:1: no section header defined


ptytest.mark.repeat(count)

如果要在代码中将某些测试用例标记为执行重复多次,可以使用 @pytest.mark.repeat(count)

@pytest.mark.repeat(5)
def test_repeat():
    print("测试用例执行")
    
    
!pytest plug/test_pytest_repeat.py::test_repeat
ERROR: /home/ubuntu/MySpace/Python/pytest/plug/pytest.ini:1: no section header defined


--repeat-scope

作用:可以覆盖默认的测试用例执行顺序,类似fixture的scope参数

!cat plug/test_pytest_repeat_scope.py
import pytest

class Test_repeat:
    def test_repeat3(self):
        print("测试用例执行333")

class Test_repeat2:
    def test_repeat3(self):
        print("测试用例执行444")
        
        
def test_repeat1():
    print("测试用例执行111")


def test_repeat2():
    print("测试用例执行222")


class Test_repeat:
    def test_repeat3(self):
        print("测试用例执行333")
!pytest -s --count=2 --repeat-scope=class plug/test_pytest_repeat_scope.py
ERROR: /home/ubuntu/MySpace/Python/pytest/plug/pytest.ini:1: no section header defined

!pytest -s --count=2 --repeat-scope=module plug/test_pytest_repeat_scope.py
ERROR: /home/ubuntu/MySpace/Python/pytest/plug/pytest.ini:1: no section header defined


注意事项

pytest-repeat不能与unittest.TestCase测试类一起使用。无论--count设置多少,这些测试始终仅运行一次,并显示警告

多重校验pytest-assume

pytest中可以用python的assert断言,也可以写多个断言,但一个失败,后面的断言将不再执行

而 pytest-assume即使中间的断言失败了,还是会执行后面的断言:

环境配置

# !pip3 install pytest-assume -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
Collecting pytest-assume
  Downloading http://pypi.doubanio.com/packages/9a/a7/bd0f0289c2978082296936c3899c77b3e738c89fa56ecbaaafd826ec2f52/pytest_assume-2.2.1-py3-none-any.whl
Collecting pytest>=2.7 (from pytest-assume)
  Downloading http://pypi.doubanio.com/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
    100% |████████████████████████████████| 256kB 56.4MB/s ta 0:00:01
[?25hCollecting more-itertools>=4.0.0 (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 43.4MB/s ta 0:00:01
[?25hCollecting wcwidth (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting py>=1.5.0 (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
    100% |████████████████████████████████| 92kB 48.0MB/s ta 0:00:01
[?25hCollecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting attrs>=17.4.0 (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting packaging (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting pluggy<1.0,>=0.12 (from pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
    100% |████████████████████████████████| 71kB 35.7MB/s ta 0:00:01
[?25hCollecting six (from packaging->pytest>=2.7->pytest-assume)
  Downloading http://pypi.doubanio.com/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Installing collected packages: more-itertools, wcwidth, py, zipp, importlib-metadata, attrs, pyparsing, six, packaging, pluggy, pytest, pytest-assume
Successfully installed attrs-19.3.0 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 pytest-assume-2.2.1 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0

assert 多重断言

def test_add1():
    assert 1 + 4 == 5
    assert 1 + 3 == 3
    assert 2 + 5 == 7
    assert 2 + 5 == 9
    print("测试完成")
    
!pytest -v -s plug/more_assert.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: rerunfailures-9.0, assume-2.2.1, repeat-0.8.0
collected 1 item                                                               

plug/more_assert.py::test_add1 FAILED

=================================== FAILURES ===================================
__________________________________ test_add1 ___________________________________

    def test_add1():
        assert 1 + 4 == 5
>       assert 1 + 3 == 3
E       assert 4 == 3
E         +4
E         -3

plug/more_assert.py:3: AssertionError
=========================== short test summary info ============================
FAILED plug/more_assert.py::test_add1 - assert 4 == 3
============================== 1 failed in 0.11s ===============================

pytest.assume多重断言

import pytest

def test_add2():
    pytest.assume(1 + 4 == 5)
    pytest.assume(1 + 3 == 3)
    pytest.assume(2 + 5 == 7)
    pytest.assume(2 + 5 == 9)
    print("测试完成")
    
!pytest -v -s plug/more_pytest_assume.py
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: rerunfailures-9.0, assume-2.2.1, repeat-0.8.0
collected 1 item                                                               

plug/more_pytest_assume.py::test_add2 测试完成
FAILED

=================================== FAILURES ===================================
__________________________________ test_add2 ___________________________________

tp = <class ‘pytest_assume.plugin.FailedAssumption‘>, value = None, tb = None

    def reraise(tp, value, tb=None):
        try:
            if value is None:
                value = tp()
            if value.__traceback__ is not tb:
>               raise value.with_traceback(tb)
E               pytest_assume.plugin.FailedAssumption: 
E               2 Failed Assumptions:
E               
E               plug/more_pytest_assume.py:5: AssumptionFailure
E               >>	pytest.assume(1 + 3 == 3)
E               AssertionError: assert False
E               
E               plug/more_pytest_assume.py:7: AssumptionFailure
E               >>	pytest.assume(2 + 5 == 9)
E               AssertionError: assert False

../../../.local/lib/python3.6/site-packages/six.py:702: FailedAssumption
=========================== short test summary info ============================
FAILED plug/more_pytest_assume.py::test_add2 - pytest_assume.plugin.FailedAss...
============================== 1 failed in 0.14s ===============================

分布式测试pytest-xdist

应用场景

分布式执行用例设计原则

环境配置

# !pip3 install pytest-xdist -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
Collecting pytest-xdist
  Downloading http://pypi.doubanio.com/packages/7c/8c/7f93c1d82f25a69a1c6e68189b9cf5ddce08dcaefdbd913d328b0234e13b/pytest_xdist-1.31.0-py2.py3-none-any.whl
Collecting pytest>=4.4.0 (from pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
    100% |████████████████████████████████| 256kB 25.6MB/s ta 0:00:01
[?25hCollecting execnet>=1.1 (from pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/d3/2e/c63af07fa471e0a02d05793c7a56a9f7d274a8489442a5dc4fb3b2b3c705/execnet-1.7.1-py2.py3-none-any.whl
Collecting pytest-forked (from pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/03/1e/81235e1fcfed57a4e679d34794d60c01a1e9a29ef5b9844d797716111d80/pytest_forked-1.1.3-py2.py3-none-any.whl
Collecting six (from pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Collecting more-itertools>=4.0.0 (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 49.0MB/s ta 0:00:01
[?25hCollecting pluggy<1.0,>=0.12 (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting wcwidth (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting packaging (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting py>=1.5.0 (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
    100% |████████████████████████████████| 92kB 15.7MB/s ta 0:00:01    73% |███████████████████████▌        | 61kB 30.9MB/s eta 0:00:01
[?25hCollecting attrs>=17.4.0 (from pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting apipkg>=1.4 (from execnet>=1.1->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/67/08/4815a09603fc800209431bec5b8bd2acf2f95abdfb558a44a42507fb94da/apipkg-1.5-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest>=4.4.0->pytest-xdist)
  Downloading http://pypi.doubanio.com/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
    100% |████████████████████████████████| 71kB 33.0MB/s ta 0:00:01
[?25hInstalling collected packages: more-itertools, zipp, importlib-metadata, pluggy, wcwidth, pyparsing, six, packaging, py, attrs, pytest, apipkg, execnet, pytest-forked, pytest-xdist
Successfully installed apipkg-1.5 attrs-19.3.0 execnet-1.7.1 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 pytest-forked-1.1.3 pytest-xdist-1.31.0 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0

pytest-xdist分布式测试原理

xdist的分布式类似于一主多从的结构,master机负责下发命令,控制slave机;slave机根据master机的命令执行特定测试任务
在xdist中,主是master,从是workers

大致原理

pytest-xdist分布式测试流程

创建worker

收集测试项用例

注意:所以为什么上面通过分布式测试的结果截图是没有输出用例的print内容,因为主机并不执行测试用例,pycharm相当于一个master

master 检查 workers 收集到的测试用例集

测试用例分发

--dist-mode选项

注意:可以使用 pytest_xdist_make_scheduler 这个hook来实现自定义测试分发逻辑。

测试用例的执行

测试用例再分发(--dist-mode=load)

测试结束

pytest-xdist通过独特的测试模式扩展pytest

pytest-xdist按照一定的顺序执行

pytest-xdist默认是无序执行的,可以通过 --dist 参数来控制顺序

--dist=loadscope

--dist=loadfile

按照同一个文件名来分组,然后将每个测试组发给可以执行的worker,确保同一个组的测试用例在同一个进程中执行

如何让scope=session的fixture在test session中仅仅执行一次

pytest-xdist是让每个worker进程执行属于自己的测试用例集下的所有测试用例

这意味着在不同进程中,不同的测试用例可能会调用同一个scope范围级别较高(例如session)的fixture,该fixture则会被执行多次,这不符合scope=session的预期

解决方案:虽然pytest-xdist没有内置的支持来确保会话范围的夹具仅执行一次,但是可以通过使用锁定文件进行进程间通信来实现。

import pytest
from filelock import FileLock

@pytest.fixture(scope="session")
def login():
    print("====登录功能,返回账号,token===")
    with FileLock("session.lock"):
        name = "testyy"
        token = "npoi213bn4"
        # web ui自动化
        # 声明一个driver,再返回

        # 接口自动化
        # 发起一个登录请求,将token返回都可以这样写

    yield name, token
    print("====退出登录!!!====")

实例

"""测试项目架构"""
!tree plug/pytest_xdist/
plug/pytest_xdist/
├── conftest.py
├── test_1.py
├── test_job
│?? ├── conftest.py
│?? └── test_case1.py
├── test_toutiao
│?? └── test_case2.py
├── test_weibo
│?? ├── conftest.py
│?? └── test_case3.py
└── untitled.txt

3 directories, 8 files
"""不使用分布式执行测试用例"""
!pytest -s plug/pytest_xdist/
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: xdist-1.31.0, rerunfailures-9.0, assume-2.2.1, forked-1.1.3, repeat-0.8.0
collected 30 items                                                             

plug/pytest_xdist/test_1.py ====登录功能,返回账号,token===
FFFFF
plug/pytest_xdist/test_job/test_case1.py EEEEEEEEEE
plug/pytest_xdist/test_toutiao/test_case2.py ==没有__init__测试用例,我进入头条了== (‘testyy‘, ‘npoi213bn4‘)
.==没有__init__测试用例,我进入头条了== (‘testyy‘, ‘npoi213bn4‘)
.==没有__init__测试用例,我进入头条了== (‘testyy‘, ‘npoi213bn4‘)
.==没有__init__测试用例,我进入头条了== (‘testyy‘, ‘npoi213bn4‘)
.==没有__init__测试用例,我进入头条了== (‘testyy‘, ‘npoi213bn4‘)
.
plug/pytest_xdist/test_weibo/test_case3.py &&& 用户 testyy 返回微博首页 &&&
查看微博热搜 0
.&&& 用户 testyy 返回微博首页 &&&
查看微博热搜 1
.&&& 用户 testyy 返回微博首页 &&&
查看微博热搜 2
.&&& 用户 testyy 返回微博首页 &&&
查看微博热搜 3
.&&& 用户 testyy 返回微博首页 &&&
查看微博热搜 4
.&&& 用户 testyy 返回微博首页 &&&
查看微博范冰冰 0
.&&& 用户 testyy 返回微博首页 &&&
查看微博范冰冰 1
.&&& 用户 testyy 返回微博首页 &&&
查看微博范冰冰 2
.&&& 用户 testyy 返回微博首页 &&&
查看微博范冰冰 3
.&&& 用户 testyy 返回微博首页 &&&
查看微博范冰冰 4
.====退出登录!!!====


==================================== ERRORS ====================================
______________________ ERROR at setup of test_case2_01[0] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[1] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[2] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[3] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[4] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_02[0] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[1] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[2] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[3] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[4] ______________________
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
=================================== FAILURES ===================================
_______________________________ test_get_info[0] _______________________________

login = (‘testyy‘, ‘npoi213bn4‘), n = 0

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[1] _______________________________

login = (‘testyy‘, ‘npoi213bn4‘), n = 1

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[2] _______________________________

login = (‘testyy‘, ‘npoi213bn4‘), n = 2

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[3] _______________________________

login = (‘testyy‘, ‘npoi213bn4‘), n = 3

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[4] _______________________________

login = (‘testyy‘, ‘npoi213bn4‘), n = 4

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
=========================== short test summary info ============================
FAILED plug/pytest_xdist/test_1.py::test_get_info[0] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[1] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[2] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[3] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[4] - NameError: name ‘sleep...
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[0]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[1]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[2]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[3]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[4]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[0]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[1]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[2]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[3]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[4]
=================== 5 failed, 15 passed, 10 errors in 15.27s ===================
"""分布式用例执行"""
!pytest -s -n auto plug/pytest_xdist/
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: xdist-1.31.0, rerunfailures-9.0, assume-2.2.1, forked-1.1.3, repeat-0.8.0
gw0 [30]m
FFFFFEEEEEEEEEE...............
==================================== ERRORS ====================================
______________________ ERROR at setup of test_case2_01[0] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[1] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[2] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[3] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[4] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_02[0] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[1] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[2] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[3] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[4] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
=================================== FAILURES ===================================
_______________________________ test_get_info[0] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 0

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[1] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 1

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[2] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 2

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[3] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 3

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[4] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 4

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
=========================== short test summary info ============================
FAILED plug/pytest_xdist/test_1.py::test_get_info[0] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[1] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[2] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[3] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[4] - NameError: name ‘sleep...
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[0]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[1]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[2]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[3]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[4]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[0]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[1]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[2]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[3]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[4]
=================== 5 failed, 15 passed, 10 errors in 15.96s ===================
"""指定需要多少个CPU来跑用例"""
!pytest -s -n 2 plug/pytest_xdist/
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: xdist-1.31.0, rerunfailures-9.0, assume-2.2.1, forked-1.1.3, repeat-0.8.0
gw0 [30] / gw1 [30]1m
FFFFFEEEEEEEEEE...............
==================================== ERRORS ====================================
______________________ ERROR at setup of test_case2_01[0] ______________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[1] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_01[2] ______________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_02[1] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_01[3] ______________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_02[2] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_01[4] ______________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 6
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_01(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:6
______________________ ERROR at setup of test_case2_02[3] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[4] ______________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
______________________ ERROR at setup of test_case2_02[0] ______________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3
file /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py, line 12
  @pytest.mark.parametrize("n", list(range(5)))
  def test_case2_02(open_51, n):
E       fixture ‘open_51‘ not found
>       available fixtures: __pytest_repeat_step_number, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, login, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, worker_id
>       use ‘pytest --fixtures [testpath]‘ for help on them.

/home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_job/test_case1.py:12
=================================== FAILURES ===================================
_______________________________ test_get_info[0] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 0

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[1] _______________________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 1

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[2] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 2

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[3] _______________________________
[gw1] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 3

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
_______________________________ test_get_info[4] _______________________________
[gw0] linux -- Python 3.6.9 /usr/bin/python3

login = (‘testyy‘, ‘npoi213bn4‘), n = 4

    @pytest.mark.parametrize("n", list(range(5)))
    def test_get_info(login, n):
>       sleep(1)
E       NameError: name ‘sleep‘ is not defined

plug/pytest_xdist/test_1.py:6: NameError
=========================== short test summary info ============================
FAILED plug/pytest_xdist/test_1.py::test_get_info[0] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[1] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[2] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[3] - NameError: name ‘sleep...
FAILED plug/pytest_xdist/test_1.py::test_get_info[4] - NameError: name ‘sleep...
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[0]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[1]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[2]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[1]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[3]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[2]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_01[4]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[3]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[4]
ERROR plug/pytest_xdist/test_job/test_case1.py::test_case2_02[0]
=================== 5 failed, 15 passed, 10 errors in 9.64s ====================
"""pytest-xdist和pytest-html很好的相结合"""
!pytest -s -n auto plug/pytest_xdist/ --html=plug/pytest_xdist/report.html --self-contained-html
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --html=plug/pytest_xdist/report.html --self-contained-html
  inifile: None
  rootdir: /home/ubuntu/MySpace/Python/pytest


生成HTML报告pytest-html

环境配置

"""安装插件"""
# !pip3 install pytest-html -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
Collecting pytest-html
  Downloading http://pypi.doubanio.com/packages/00/a7/34f195c514d39b4453619b3eb284989e5adb09a2a68ac09ce3779f9b9478/pytest_html-2.1.1-py2.py3-none-any.whl
Collecting pytest>=5.0 (from pytest-html)
  Downloading http://pypi.doubanio.com/packages/c7/e2/c19c667f42f72716a7d03e8dd4d6f63f47d39feadd44cc1ee7ca3089862c/pytest-5.4.1-py3-none-any.whl (246kB)
    100% |████████████████████████████████| 256kB 49.7MB/s ta 0:00:01
[?25hCollecting pytest-metadata (from pytest-html)
  Downloading http://pypi.doubanio.com/packages/ce/8f/d0542e1aa0e23d902ce6acce2790736473da94453a36bdc7829f25734199/pytest_metadata-1.8.0-py2.py3-none-any.whl
Collecting py>=1.5.0 (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/99/8d/21e1767c009211a62a8e3067280bfce76e89c9f876180308515942304d2d/py-1.8.1-py2.py3-none-any.whl (83kB)
    100% |████████████████████████████████| 92kB 51.3MB/s ta 0:00:01
[?25hCollecting attrs>=17.4.0 (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Collecting importlib-metadata>=0.12; python_version < "3.8" (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/ad/e4/891bfcaf868ccabc619942f27940c77a8a4b45fd8367098955bb7e152fb1/importlib_metadata-1.6.0-py2.py3-none-any.whl
Collecting wcwidth (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/f6/d5/1ecdac957e3ea12c1b319fcdee8b6917ffaff8b4644d673c4d72d2f20b49/wcwidth-0.1.9-py2.py3-none-any.whl
Collecting pluggy<1.0,>=0.12 (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/a0/28/85c7aa31b80d150b772fbe4a229487bc6644da9ccb7e427dd8cc60cb8a62/pluggy-0.13.1-py2.py3-none-any.whl
Collecting more-itertools>=4.0.0 (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/72/96/4297306cc270eef1e3461da034a3bebe7c84eff052326b130824e98fc3fb/more_itertools-8.2.0-py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 40.3MB/s ta 0:00:01
[?25hCollecting packaging (from pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/62/0a/34641d2bf5c917c96db0ded85ae4da25b6cd922d6b794648d4e7e07c88e5/packaging-20.3-py2.py3-none-any.whl
Collecting zipp>=0.5 (from importlib-metadata>=0.12; python_version < "3.8"->pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/b2/34/bfcb43cc0ba81f527bc4f40ef41ba2ff4080e047acb0586b56b3d017ace4/zipp-3.1.0-py3-none-any.whl
Collecting six (from packaging->pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/65/eb/1f97cb97bfc2390a276969c6fae16075da282f5058082d4cb10c6c5c1dba/six-1.14.0-py2.py3-none-any.whl
Collecting pyparsing>=2.0.2 (from packaging->pytest>=5.0->pytest-html)
  Downloading http://pypi.doubanio.com/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB)
    100% |████████████████████████████████| 71kB 39.9MB/s ta 0:00:01
[?25hInstalling collected packages: py, attrs, zipp, importlib-metadata, wcwidth, pluggy, more-itertools, six, pyparsing, packaging, pytest, pytest-metadata, pytest-html
Successfully installed attrs-19.3.0 importlib-metadata-1.6.0 more-itertools-8.2.0 packaging-20.3 pluggy-0.13.1 py-1.8.1 pyparsing-2.4.7 pytest-5.4.1 pytest-html-2.1.1 pytest-metadata-1.8.0 six-1.14.0 wcwidth-0.1.9 zipp-3.1.0

快速入门

注意事项:

"""在当前目录下创建一个report.html的测试报告"""
!pytest --html=report.html
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: xdist-1.31.0, rerunfailures-9.0, html-2.1.1, assume-2.2.1, forked-1.1.3, metadata-1.8.0, repeat-0.8.0
collected 39 items / 1 error / 38 selected                                     

==================================== ERRORS ====================================
_________________ ERROR collecting plug/pytest_xdist/test_1.py _________________
import file mismatch:
imported module ‘test_1‘ has this __file__ attribute:
  /home/ubuntu/MySpace/Python/pytest/conftest/test_1.py
which is not the same as the test file we want to collect:
  /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_1.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
-- generated html file: file:///home/ubuntu/MySpace/Python/pytest/report.html --
=========================== short test summary info ============================
ERROR plug/pytest_xdist/test_1.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
=============================== 1 error in 0.23s ===============================
"""css是独立的,分享报告的时候样式会丢失,为了更好的分享发邮件展示报告,把css样式合并到html里"""
!pytest --html=report.html --self-contained-html
============================= test session starts ==============================
platform linux -- Python 3.6.9, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
rootdir: /home/ubuntu/MySpace/Python/pytest
plugins: xdist-1.31.0, rerunfailures-9.0, html-2.1.1, assume-2.2.1, forked-1.1.3, metadata-1.8.0, repeat-0.8.0
collected 39 items / 1 error / 38 selected                                     

==================================== ERRORS ====================================
_________________ ERROR collecting plug/pytest_xdist/test_1.py _________________
import file mismatch:
imported module ‘test_1‘ has this __file__ attribute:
  /home/ubuntu/MySpace/Python/pytest/conftest/test_1.py
which is not the same as the test file we want to collect:
  /home/ubuntu/MySpace/Python/pytest/plug/pytest_xdist/test_1.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
-- generated html file: file:///home/ubuntu/MySpace/Python/pytest/report.html --
=========================== short test summary info ============================
ERROR plug/pytest_xdist/test_1.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
=============================== 1 error in 0.17s ===============================

原文:https://www.cnblogs.com/crisimple/p/12906743.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!