pytest中文文檔


官方文檔

pytest documentation:http://www.pytest.org/en/latest/

第一章 安裝和啟動

pytest作為一個測試框架,可以非常簡單的建立易用性好,擴展性強的測試集。這些測試因為避免了

大量的樣板代碼,所以可讀性非常高。

你可以花費一點時間通過一個unittest或者略復雜的函數測試來驗證你的應用程序或者庫

1.1 安裝pytest

1.在你的python環境下運行下面的命令即可安裝pytest

pip install ‐U pytest
2.檢查你安裝的pytest的版本信息是否正確:
pip show pytest

pytest --version

1.2 創建你的第一個測試

創建一個只有4行代碼的簡單函數:

# test_sample.py 的內容 
def func(x):
    return x + 1


def test_answer():
    assert func(3) == 5
現在,你可以在test_sample.py的同級目錄下直接執行pytest,結果如下:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest
================================================= test session starts =================================================
platform win32 -- Python 3.7.1, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\DiluWorkspace\Code\MyTest
collected 1 item

test_sample.py F                                                                                                 [100%]

====================================================== FAILURES =======================================================
_____________________________________________________ test_answer _____________________________________________________

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

test_sample.py:7: AssertionError
================================================== 1 failed in 0.02s ==================================================

(pytest_env) D:\DiluWorkspace\Code\MyTest>
這個測試的結果是失敗的,因為func(3)的返回值不是5

1.3 運行多個測試

pytest會運行當前目錄及子目錄下所有以 test_*.py 和 *_test.py 命名的文件。文件匹配方式遵循
Standard test discovery rules

1.4 判斷是否發生了指定的異常

使用raises可以判斷代碼是否拋出了異常:
# test_sysexit.py 的內容
import pytest


def f():
    raise SystemExit(1)


def test_my_test():
    with pytest.raises(SystemExit):
        f()
使用"quiet"模式來執行這個測試:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_sysexit.py
.                                                                                                                [100%]
1 passed in 0.02s

1.5 將多個測試用例放在一個class中

當你需要開發多個測試用例的時候,你可能需要將他們放在同一個class中,pytest可以很簡單的創建

包含多個測試用例的class:
# test_class.py的內容
class TestClass(object):
    def test_one(self):
        x = 'this'
        assert 'h' in x

    def test_two(self):
        x = 'hello'
        assert hasattr(x, 'check')
pytest根據Conventions for Python test discovery查找所有的測試用例,所以可以找到所有以
**test_**開頭的測試函數。我們可以通過文件名來直接運行整個模塊:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_sysexit.py
.                                                                                                                [100%]
1 passed in 0.02s

(pytest_env) D:\DiluWorkspace\Code\MyTest>
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________

self = <test_class.TestClass object at 0x000002012A089550>

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

test_class.py:9: AssertionError
1 failed, 1 passed in 0.03s
第一個測試用例passed,第二個測試用例failed。你可以很直觀的觀察到測試用例中進行判斷的中間
值,這可以幫助理解測試用例失敗的原因。

1.6 為測試創建唯一的臨時文件夾

pytest 提供 Builtin fixtures/function arguments來創建任意的資源,比如一個具有唯一的臨時文件
夾:
# test_tmpdir.py的內容
def test_needs_files(tmpdir):
    print(tmpdir)
    assert 0
如果函數的簽名中(函數簽名包括函數的參數和返回值,以及參數的封送順序等等)包含參數tmpdir,
pytest就會在執行測試用例之前查找並調用特定的fixture創建所需資源。在本例中,pytest會創建一
個unique-per-test-invocation臨時文件夾:
pytest ‐‐fixtures
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_tmpdir.py
F                                                                                                                [100%]
====================================================== FAILURES =======================================================
__________________________________________________ test_needs_files ___________________________________________________

tmpdir = local('C:\\Users\\lcgst\\AppData\\Local\\Temp\\pytest-of-lcgst\\pytest-0\\test_needs_files0')

    def test_needs_files(tmpdir):
        print(tmpdir)
>       assert 0
E       assert 0

test_tmpdir.py:4: AssertionError
------------------------------------------------ Captured stdout call -------------------------------------------------
C:\Users\lcgst\AppData\Local\Temp\pytest-of-lcgst\pytest-0\test_needs_files0
1 failed in 0.03s
關於tmpdir的更多信息請參考Temporary directories and files 通過下面的命令可以查看所有內置
的pytest fixture:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest --fixtures
================================================= test session starts =================================================
platform win32 -- Python 3.7.1, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\DiluWorkspace\Code\MyTest
collected 5 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.07s ================================================

第二章 用法

2.1 通過python -m pytest調用pytest

這是在2.0版本中新引入的功能。 你可以通過python的解釋器,利用命令行來調用測試:
 
python ‐m pytest [...]
這種調用方式幾乎等同於直接調用pytest […],但需要注意的是這種通過python來調用的方式同時會
將當前目錄添加到sys.path

2.2 退出碼

pytest有以下6種退出碼:
Exit code 0: 找到所有測試用例並測試通過 
Exit code 1: 找到測試用例並運行但是部分測試用例運行失敗 
Exit code 2: 用戶中斷了測試
Exit code 3: 執行過程中發生了內部錯誤 
Exit code 4: pytest命令行使用錯誤 
Exit code 5: 沒有找到任何測試用例

2.3 版本信息,參數名,環境變量的幫助

pytest ‐‐version #顯示pytest的import的路徑
pytest ‐‐fixtures #顯示內置的函數參數 
pytest ‐h | ‐‐help #幫助信息

2.4 第一(N)次測試失敗后停止

使用下面的參數可以讓測試在第1(N)次測試失敗后停止:
pytest ‐x # 第一次測試失敗后停止測試 
pytest ‐‐maxfail=2 # 第2次測試失敗后停止測試

2.5 指定/選擇測試用例

Pytest在命令行中支持多種方式來運行和選擇測試用例:
對模塊中進行測試:
pytest test_mod.py
對文件夾中進行測試:
pytest testing/
通過關鍵字表達式來進行測試:
pytest ‐k "MyClass and not method"
這種方式會執行文件名,類名以及函數名與給定的字符串表達式相匹配的測試用例。 上面的用例會執行TestMyClass.test_something但是不會執行TestMyClass.test_method_simple。and not 在這里是表達式,未經過測試
通過節點id來進行測試
每個被選中的測試用例都會被分配一個唯一的nodeid,它由模塊文件名和以下說明符組成:參數化的類名、函數名和參數,用::分隔。
可以通過下面的方式運行模塊中的指定的測試用例:
pytest test_mod.py::test_func
也可以通過下面這種方式:
pytest test_mod.py::TestClass::test_method
通過標記符來進行測試
pytest ‐m slow
這種方式會運行所有通過裝飾器 @pytest.mark.slow進行裝飾的測試用例。
關於標記符請參考marks
通過包來運行
pytest ‐‐pyargs pkg.testing
這種方式會導入pkg.testing,並且基於該包所在位置來查找並運行測試用例。

2.6 修改python的traceback的打印

pytest ‐‐showlocals  #在tracebacks中顯示本地變量 
pytest ‐l            #同上 
pytest ‐‐tb=auto     #(默認顯示方式)該方式會顯示tracebacks的第一條和最后一條的詳細信息 
                    #其余的信息會簡略顯示 
pytest ‐‐tb=long     #詳細顯示所有的tracebacks 
pytest ‐‐tb=short    #簡略顯示tracebacks 
pytest ‐‐tb=line     #每個錯誤只顯示一行
pytest ‐‐tb=native  #以python標准庫模式輸出 
pytest ‐‐tb=no       #不輸出tracebacks                                        
另外還有比**–tb=long輸出更詳細的參數–full-trace**。在該參數下,KeyboardInterrupt(Ctrl+C)在中斷traceback的輸出的時候同時會打印棧信息。 這在測試耗時過長的時候非常有用,使用該組合可以幫助我們找到測試用例掛死在何處。
如果使用默認方式,測試被中斷的時候不會有任何棧信息輸出(因為KeyboardInterrupt被pytest捕獲了),使用該參數可以保證traceback能夠顯示出來。
例子:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --showlocals test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________

self = <test_class.TestClass object at 0x0000015D742CC908>

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

self       = <test_class.TestClass object at 0x0000015D742CC908>
x          = 'hello'

test_class.py:9: AssertionError
1 failed, 1 passed in 0.03s

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q -l test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________

self = <test_class.TestClass object at 0x00000117BB4B9940>

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

self       = <test_class.TestClass object at 0x00000117BB4B9940>
x          = 'hello'

test_class.py:9: AssertionError
1 failed, 1 passed in 0.03s

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=auto test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________

self = <test_class.TestClass object at 0x000001F2A992A7B8>

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

test_class.py:9: AssertionError
1 failed, 1 passed in 0.03s

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=auto test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________

self = <test_class.TestClass object at 0x000001BC93029898>

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

test_class.py:9: AssertionError
1 failed, 1 passed in 0.03s

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=long test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________

self = <test_class.TestClass object at 0x0000027AEE435978>

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

test_class.py:9: AssertionError
1 failed, 1 passed in 0.02s

(pytest_env) D:\DiluWorkspace\Code\MyTest>
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=short test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________
test_class.py:9: in test_two
    assert hasattr(x, 'check')
E   AssertionError: assert False
E    +  where False = hasattr('hello', 'check')
1 failed, 1 passed in 0.03s

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=line test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
D:\DiluWorkspace\Code\MyTest\test_class.py:9: AssertionError: assert False
1 failed, 1 passed in 0.02s

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=native test_class.py
.F                                                                                                               [100%]
====================================================== FAILURES =======================================================
_________________________________________________ TestClass.test_two __________________________________________________
Traceback (most recent call last):
  File "D:\DiluWorkspace\Code\MyTest\test_class.py", line 9, in test_two
    assert hasattr(x, 'check')
AssertionError: assert False
 +  where False = hasattr('hello', 'check')
1 failed, 1 passed in 0.01s

(pytest_env) D:\DiluWorkspace\Code\MyTest>
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q --tb=no test_class.py
.F                                                                                                               [100%]
1 failed, 1 passed in 0.02s

2.7 詳盡的測試報告

這是2.9的新功能。
參數-r可以用來在測試結束后展示一份“測試概要信息”,這使得在大型測試套中獲取一份清楚
的測試結果(失敗,跳過等測試信息)十分簡單。
示例
# test_example.py的內容
import pytest


@pytest.fixture
def error_fixture():
    assert 0


def test_ok():
    print("ok")


def test_fail():
    assert 0


def test_error(error_fixture):
    pass


def test_skip():
    pytest.skip("skipping this test")


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


@pytest.mark.xfail(reason="always xfail")
def test_xpass():
    pass

執行

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_example.py -ra
.FEsxX                                                                                                           [100%]
======================================================= ERRORS ========================================================
____________________________________________ ERROR at setup of test_error _____________________________________________

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

test_example.py:7: AssertionError
====================================================== FAILURES =======================================================
______________________________________________________ test_fail ______________________________________________________

    def test_fail():
>       assert 0
E       assert 0

test_example.py:15: AssertionError
=============================================== short test summary info ===============================================
SKIPPED [1] D:\DiluWorkspace\Code\MyTest\test_example.py:23: skipping this test
XFAIL test_example.py::test_xfail
  reason: xfailing this test
XPASS test_example.py::test_xpass always xfail
ERROR test_example.py::test_error - assert 0
FAILED test_example.py::test_fail - assert 0
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.05s
-r后可以追加一些參數,上面示例中的a表示"除了passes的所有信息"。
可以追加的參數列表如下:
f - failed 
E - error 
s - skipped 
x - xfailed 
X - xpassed 
p - passed 
P - passed with output 
a - all except pP
可以同時追加多個參數,如果你想只看"failed"和"skipped"的測試結果,你可以執行:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_example.py -rfs
.FEsxX                                                                                                           [100%]
======================================================= ERRORS ========================================================
____________________________________________ ERROR at setup of test_error _____________________________________________

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

test_example.py:7: AssertionError
====================================================== FAILURES =======================================================
______________________________________________________ test_fail ______________________________________________________

    def test_fail():
>       assert 0
E       assert 0

test_example.py:15: AssertionError
=============================================== short test summary info ===============================================
FAILED test_example.py::test_fail - assert 0
SKIPPED [1] D:\DiluWorkspace\Code\MyTest\test_example.py:23: skipping this test
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.04s
p可以用來顯示pass的測試用例,P會在測試報告中增加一段"PASSES"的信息來顯示通過的測試用
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest -q test_example.py -rpP
.FEsxX                                                                                                           [100%]
======================================================= ERRORS ========================================================
____________________________________________ ERROR at setup of test_error _____________________________________________

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

test_example.py:7: AssertionError
====================================================== FAILURES =======================================================
______________________________________________________ test_fail ______________________________________________________

    def test_fail():
>       assert 0
E       assert 0

test_example.py:15: AssertionError
======================================================= PASSES ========================================================
_______________________________________________________ test_ok _______________________________________________________
------------------------------------------------ Captured stdout call -------------------------------------------------
ok
=============================================== short test summary info ===============================================
PASSED test_example.py::test_ok
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.05s

2.8 測試失敗時自動調用PDB

pytest允許通過命令行使能在測試失敗時自動調用python的內置調試工具PDB:
pytest ‐‐pdb
這會在每次測試失敗(或者發生KeyboardInterrupt)的時候都去調用PDB。通常我們可能只需要在第
一次測試失敗的時候來調用pdb:
pytest ‐x ‐‐pdb #首次失敗的時候調用pdb,然后結束測試進程
pytest ‐‐pdb ‐‐maxfail=3 #前三次失敗的時候調用pdb
注意所有的異常信息都會保存在sys.last_value, sys.last_type和sys.last_traceback中。 在交互使用
中,這允許使用任何調試工具進入后期調試。我們也可以手動的訪問這些異常信息,如下:
(Pdb) import sys
(Pdb) sys.last_traceback.tb_lineno 
1448 
(Pdb) sys.last_value
AssertionError('assert 0') 
(Pdb) sys.last_type
<class 'AssertionError'>

2.9 測試啟動時調用PDB

pytest允許在測試啟動時立即調用pdb:
pytest ‐‐trace
這會在每個測試開始時立即調用python的pdb

2.10 設置斷點

在代碼中使用python的原生接口python import pdb; pdb.set_trace()來設置斷點,在pytest中會自動禁用該測試的輸出捕獲:
其他測試的輸出捕獲不會受影響
先前的測試中已經被捕獲的輸出會被正常處理
同一測試中的后續輸出不會被捕獲,而被轉發給sys.stdout。注意即使退出交互式pdb繼續運行測試,這一設置依然生效

2.11 使用內置的斷點函數

python3.7 引入了內置的斷點函數 breakpoint(),pytest支持該函數:
當breakpoint()被調用,並且PYTHONBREAKPOINT是默認值時,pytest將會使用內部自定義的PDB UI, 而不是系統默認的PDB。測試完成后,返回系統默認的PDB UI 當使用–pdb參數時,breakpoint()和測試異常時都會使用內部自定義的PDB UI
–pdbcls 可以用於指定自定義的調試類

2.12 分析測試時間

顯示最慢的10個測試步驟:
pytest ‐‐durations=10
默認情況下,如果測試時間很短(<0.01s),這里不會顯示執行時常,如果需要顯示,在命令行中追加-vv參數

2.13 創建 JUnitXML 格式的文件

使用下面的方式可以創建Jekins或者其他的集成測試環境可讀的結果文件:
pytest ‐‐junitxml=path
path是生成的XML文件
3.1引入的新特性:
可以在pytest的配置文件中設置junit_suite_name屬性來配置測試結果XML中的root名稱
[pytest] 
junit_suite_name = my_suite
4.0引入的新特性:
Junit XML規范中"time"屬性應該是總的測試執行的時間,包含setup和teardown。這也是pytest的默認行為。如果想要只顯示測試的時間(call duration),配置junit_duration_report:
[pytest] 
junit_duration_report = call

2.13.1 record_property

2.13.2 record_xml_attribute

3.4引入
使用record_xml_attribute可以在測試用例中增加額外的xml屬性。該固件也可以用來復寫已經存在的屬性值
def test_function(record_xml_attribute): 
    record_xml_attribute("assertions", "REQ‐1234") 
    record_xml_attribute("classname", "custom_classname")
    print("hello world") 
    assert True
與record_property不同,該固件不會新增子節點,而是在testcase的tag里面增加或者覆蓋已有的屬性:
<testcase classname="custom_classname" file="test_function.py" line="0" 
          name="test_function" time="0.003" assertions="REQ‐1234">
    <system‐out> 
    hello world 
    </system‐out>
</testcase>
注意: record_xml_attribute尚處於實驗階段,將來可能會有所變化。

2.13.3 LogXML:add_global_property

2.14 創建resultlog格式文件

很少用,很快就要被移除了

2.15 將測試結果發送給在線的pastebin

Pastebin是一個用戶存儲純文本的web應用。用戶可以通過互聯網分享文本片段,一般都是源代碼。

2.16 禁用插件

如果想要在運行時禁用特定的插件,使用 -p以及no:前綴符。
如: 禁止加載doctest插件
pytest ‐p no:doctest

2.17 在python代碼中運行pytest

2.0引入 你可以在python代碼中直接調用pytest:
pytest.main()
這和在命令行中使用pytest是一樣的, 這種方式不會拋出SystemExit異常,而會返回exitcode, 通過如下方式可以傳入調用參數:
pytest.main(['‐x', 'mytestdir'])
你可以在pytest.main中指定額外的插件:
# myinvoke.py的內容 
import pytest


class MyPlugin(object):
    def pytest_sessionfinish(self):
        print("*** test run reporting finishing")


pytest.main(["‐qq"], plugins=[MyPlugin()])
調用pytest.main()會導入你的測試用例及其所引用的所有的模塊。因為python存在模塊導入的緩存機制,如果多次調用pytest.main(),后續的調用也不會再刷新這些導入的資源。因此,不建議再同一進程中多次調用pytest.main() (比如重新運行測試).

第三章 在現有測試套中使用pytest

pytest可以與大多數現有的測試套一起使用,但他的測試行為與其他的測試工具(如nose或者python的默認的unittest)有差異. 在使用此部分之前,您需要安裝pytest

3.1 與現有的測試套一起運行pytest

比如說你想要修改某處的已有的代碼庫,在將代碼拉到你的開發環境后並且設置好python的環境后,你需要在你的工程的根目錄下運行
cd <repository> 
pip install ‐e . #解決環境依賴可以通過"python setup.py develop"或"conda develop"
這將在site-packages中設置一個symlink到你的代碼,允許你在運行測試的時候編輯代碼,就好像你已經安裝了這個package一樣。設置項目為開發模式,可以避免每次運行測試時都需要重新install。當然,也可以考慮使用tox庫。

第四章 在測試用例中編寫和上報斷言

4.1 使用斷言語句

pytest允許你在測試用例中使用標准的python斷言,如下:
# test_assert1 中的內容
def f():
    return 3


def test_function():
    assert f() == 4
本例中的函數期望返回一個固定的值。如果該斷言失敗了,你會看到該函數的返回值:
(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest test_assert1.py
================================================= test session starts =================================================
platform win32 -- Python 3.7.1, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\DiluWorkspace\Code\MyTest
collected 1 item

test_assert1.py F                                                                                                [100%]

====================================================== FAILURES =======================================================
____________________________________________________ test_function ____________________________________________________

    def test_function():
>       assert f() == 4
E       assert 3 == 4
E        +  where 3 = f()

test_assert1.py:7: AssertionError
================================================== 1 failed in 0.03s ==================================================
pytest支持顯示常見的子表達式的值,包括調用,屬性,比較以及二元和一元運算符。(參看Demoof Python failure reports with purest 這允許你使用你習慣的python的在不丟失內省信息的情況下構造代碼。如果你為斷言指定了輸出信息,那么不會輸出任何內省信息,而是在traceback中直接輸出指定的信息:
assert a % 2 ==0, "value was odd, should be even"
更多斷言內省信息請參考Advanced assertion introspection
例子:
# test_assert1 中的內容
def f():
    return 3


def test_function():
    assert f() == 4, "被測函數的值不等於4"

執行結果:

(pytest_env) D:\DiluWorkspace\Code\MyTest>pytest test_assert1.py
================================================= test session starts =================================================
platform win32 -- Python 3.7.1, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: D:\DiluWorkspace\Code\MyTest
collected 1 item

test_assert1.py F                                                                                                [100%]

====================================================== FAILURES =======================================================
____________________________________________________ test_function ____________________________________________________

    def test_function():
>       assert f() == 4, "被測函數的值不等於4"
E       AssertionError: 被測函數的值不等於4
E       assert 3 == 4
E        +  where 3 = f()

test_assert1.py:7: AssertionError
================================================== 1 failed in 0.03s ==================================================

4.2 異常的斷言

你可以在上下文中使用pytest.raises來對異常產生斷言:
import pytest

def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0
如果你還需要訪問異常的確切信息,你需要使用下面的方式:
def test_recursion_depth():
    with pytest.raises(RuntimeError) as excinfo:
        def f():
            f()
        f()
    assert 'Maximum recursion' in str(excinfo.value)
excinfo是ExceptionInfo的一個實例,里面包含了異常的詳細信息,主要屬性是.type, .value以及.traceback. 你可以通過match參數來使用正則表達式來匹配一個異常是否發生
import pytest 
def myfunc():
    raise ValueError("Exception 123 raised")      
def test_match():
    with pytest.raises(ValueError, match=r'.* 123 .*'):
    myfunc()
參數match與re.search行為是一致的,所以上面的match='123’能夠正確的匹配到myfunc拋出的 異常。
pytest.raises的另一種使用方法是傳遞一個函數給它,該函數使用給定的參數,並且最終判斷該函數 是否產生了期望的斷言
pytest.raises(ExpectedException, func, *args, **kwargs)
測試報告會提供你一些幫助信息,比如是沒有異常還是拋出了錯誤的異常 注意也可以在pytest.mark.xfail中使用raises來檢查測試用例是否是因為拋出了異常而失敗:
@pytest.mark.xfail(raises=IndexError) 
def test_f():     
    f()
使用pytest.raises更適合測試自己的代碼故意引發的異常。 而使用@pytest.mark.xfail和檢查函數更適合那些記錄的未修復的bug(也就是那些應該發生的異常) 或者依賴項中的異常。?(這兩個場景在測試過程中是不一樣的)
 
持續更新...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM