二、pytest -- 使用和調用

1. 經過python -m pytest調用pytest

你能夠經過python的解釋器來執行測試:node

python -m pytest [...]

可是,這和直接執行pytest [...]命令的效果幾乎是如出一轍的;python

2. pytest執行結束時返回的狀態碼

pytest命令執行結束,可能會返回如下六種狀態碼:git

  • 0:(OK)全部收集到的用例測試經過
  • 1:(TESTS_FAILED)有用例測試失敗
  • 2:(INTERRUPTED)用戶打斷測試執行
  • 3:(INTERNAL_ERROR)測試執行的過程當中,發生內部錯誤
  • 4:(USAGE_ERROR)pytest命令使用錯誤
  • 5:(NO_TESTS_COLLECTED)沒有收集到測試用例

它們在枚舉類 _pytest.main.ExitCode 中聲明。而且,其做爲公開API的一部分,可以直接引入和訪問:github

from pytest import ExitCode

3. 獲取幫助信息

pytest --version  # 查看版本號和pytest的引入路徑
pytest -h  # 查看幫助信息

4. 最多容許失敗的測試用例數

當達到最大上限時,退出執行;如未配置,則沒有上限:緩存

pytest -x  # 遇到第一個失敗時,退出執行
pytest --maxfail==2  # 遇到第二個失敗時,退出執行

5. 執行指定的測試用例

pytest支持多種方式來執行特定的測試用例:bash

5.1. 執行指定模塊中的測試用例

pytest test_mod.py

5.2. 執行指定目錄下全部的測試用例

pytest testing/

5.3. 執行文件名、類名或者函數名中包含特定關鍵字的測試用例

執行當前目錄下,名字包含_class但不包含two的測試用例:session

pytest -k "_class and not two" .

注意:python的關鍵字不能夠應用在-k選項中,例如,classdef等。app

5.4. 執行指定nodeid的測試用例

pytest爲每個收集到的測試用例指定一個惟一的nodeid。其由模塊名加說明符構成,中間以::間隔。函數

其中,說明符能夠是類名、函數名以及由parametrize標記賦予的參數

# src/chapter-2/test_nodeid.py

import pytest


def test_one():
    print('test_one')
    assert 1


class TestNodeId:
    def test_one(self):
        print('TestNodeId::test_one')
        assert 1

    @pytest.mark.parametrize('x,y', [(1, 1), (3, 4)])
    def test_two(self, x, y):
        print(f'TestNodeId::test_two::{x} == {y}')
        assert x == y

在上述示例中,咱們建立了三個測試用例,分別對應不一樣的說明符

  • 指定函數名執行

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

    $ pipenv run pytest -q -s src/chapter-2/test_nodeid.py::TestNodeId::test_one
    TestNodeId::test_one
    .
    1 passed in 0.01s
  • 指定parametrize標記賦予的參數執行

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

    這裏對參數xy賦值的形式是[1-1],中間以-間隔;

    單個或多個參數的賦值形式以此類比;而且,只能爲[1-1]或者[3-4],其它的會報錯;

注意:

這裏咱們也可使用-k選項達到一樣的效果:

  • 首先,可使用--collect-only選項查看用例名:

    λ pipenv run pytest -q -s --collect-only src/chapter-2/test_nodeid.py
    test_nodeid.py::test_one
    test_nodeid.py::TestNodeId::test_one
    test_nodeid.py::TestNodeId::test_two[1-1]
    test_nodeid.py::TestNodeId::test_two[3-4]
  • 而後,使用-k執行符合規則的用例,例如:執行test_nodeid.py::test_one

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

    結果和執行pipenv run pytest -q -s src/chapter-2/test_nodeid.py::test_one同樣;

5.5. 執行指定標記的用例

pytest -m slow

5.6. 執行指定包中的測試用例

pytest --pyargs pkg.testing

pytest會引入pkg.testing包,並在它的系統目錄下搜尋測試用例並執行;

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

pytest回溯信息的輸出一共有六種模式:auto/long/short/line/native/no,用--tb選項指定:

pytest -l, --showlocals         # 打印本地變量
pytest --tb=auto                # 默認模式
pytest --tb=long                # 儘量詳細的輸出
pytest --tb=short               # 更簡短的輸出
pytest --tb=line                # 每一個失敗信息總結在一行中
pytest --tb=native              # python的標準輸出
pytest --tb=no                  # 不打印失敗信息

--full-trace是一種比--tb=long更詳細的輸出模式。它甚至能觀察到用戶打斷執行(Ctrl+C)時的回溯信息,而上述六種模式默認是不輸出此類信息的。

7. 總結報告

-r選項能夠在執行結束後,打印一個簡短的總結報告。在執行的測試用例不少時,可讓你對結果有個清晰的瞭解:

# src/chapter-2/test_report.py

import pytest


@pytest.fixture
def error_fixture():
    assert 0


def test_ok():
    print("ok")


def test_fail():
    assert 0


def test_error(error_fixture):
    pass


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


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


@pytest.mark.xfail(reason="always xfail")
def test_xpass():
    pass
$ pipenv run pytest -q -rA src/chapter-2/test_report.py 
.FEsxX                                                            [100%]
================================ ERRORS =================================
_____________________ ERROR at setup of test_error ______________________

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

src/chapter-2/test_report.py:27: AssertionError
=============================== FAILURES ================================
_______________________________ test_fail _______________________________

    def test_fail():
>       assert 0
E       assert 0

src/chapter-2/test_report.py:35: AssertionError
================================ PASSES =================================
________________________________ test_ok ________________________________
------------------------- Captured stdout call --------------------------
ok
======================== short test summary info ========================
PASSED src/chapter-2/test_report.py::test_ok
SKIPPED [1] /Users/yaomeng/Private/Projects/pytest-chinese-doc/src/chapter-2/test_report.py:44: skipping this test
XFAIL src/chapter-2/test_report.py::test_xfail
  reason: xfailing this test
XPASS src/chapter-2/test_report.py::test_xpass always xfail
ERROR src/chapter-2/test_report.py::test_error - assert 0
FAILED src/chapter-2/test_report.py::test_fail - assert 0
1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.08s

-r選項後面要緊接這一個參數,用於過濾顯示測試用例的結果。

如下是全部有效的字符參數:

  • f:失敗的
  • E:出錯的
  • s:跳過執行的
  • x:跳過執行,並標記爲xfailed的
  • X:跳過執行,並標記爲xpassed的
  • p:測試經過的
  • P:測試經過,而且有輸出信息的;即用例中有print
  • a:除了測試經過的,其餘全部的;即除了pP
  • A:全部的

上述字符參數能夠疊加使用,例如:咱們指望過濾出失敗的和未執行的:

pytest -rfs

8. 失敗時加載PDB(Python Debugger)環境

PDBpython內建的診斷器,pytest容許經過如下命令在執行失敗時進入這個診斷器模式:

pytest --pdb

pytest會在測試用例失敗(或者Ctrl+C)時,調用這個診斷器:

# src/chapter-2/test_pdb.py

def test_fail():
    x = 1
    assert x == 0
$ pipenv run pytest -q --pdb src/chapter-2/test_pdb.py 
F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    def test_fail():
        x = 1
>       assert x == 0
E       assert 1 == 0

src/chapter-2/test_pdb.py:25: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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

你還能夠訪問測試用例的本地變量x

失敗的信息存儲在sys.last_value, sys.last_type, sys.last_traceback變量中,你能夠在交互環境中訪問它們;

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

9. 開始執行時就加載PDB環境

經過如下命令,pytest容許你在每一個測試用例開始執行時,就加載PDB環境:

pytest --trace

10. 設置斷點

在測試用例代碼中添加import pdb;pdb.set_trace(),當其被調用時,pytest會中止這條用例的輸出:

  • 其餘用例不受影響;
  • 經過continue命令,退出PDB環境,並繼續執行用例;

11. 使用內置的中斷函數

python 3.7介紹了一個內置breakpoint()函數。pytest能夠在如下場景中支持使用:

  • breakpoint()被調用,而且PYTHONBREAKPOINTNone時,pytest會使用內部自定義的PDB代替系統的;
  • 測試執行結束時,自動切回系統自帶的PDB
  • 當加上--pdb選項時,breakpoint()和測試發生錯誤時,都會調用內部自定義的PDB
  • --pdbcls選項容許指定一個用戶自定義的PDB類;

12. 分析測試執行時間

獲取執行最慢的10個測試用例:

pytest --durations=10

默認狀況下,pytest不會顯示執行時間<0.01s的測試用例,可使用-vv選項查看它們;

13. 錯誤句柄

5.0版本新增特性

在測試執行中發生段錯誤或者超時的狀況下,faulthandler標準模塊能夠轉儲python的回溯信息;

它在pytest的執行中默認使能,使用-p no:faulthandler選項能夠關閉它;

一樣,faulthandler_timeout=X配置項,可用於當測試用例的完成時間超過X秒時,轉儲全部線程的python回溯信息:

# src/chapter-2/pytest.ini

[pytest]
faulthandler_timeout=5

配置測試執行的超時時間是5秒;

# test_fault_handler.py 

import time


def test_faulthandler():
    time.sleep(7)
    assert 1

測試用例中添加等待7秒的操做;

  • 默認使能faulthandler的狀況:

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

    在執行剛超過5秒的時候會打印出回溯信息。但不會中斷測試的執行;

  • 去使能faulthandler的狀況:

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

    超時並不會觸發回溯信息的打印;

注意:

這個功能是從pytest-faulthandler插件合併而來的,可是有兩點不一樣:

  • 去使能時,使用-p no:faulthandler代替原來的--no-faulthandler;
  • 使用faulthandler_timeout配置項代替--faulthandler-timeout命令行選項來配置超時時間。固然,你也可使用-o faulthandler_timeout=X在命令行配置;

14. 建立JUnitXML格式的測試報告

使用以下命令,能夠在指定的path中建立一個能被Jenkins或者其餘CI工具讀取的XML格式的測試報告:

pytest --junitxml=path

你能夠在項目的pytest.ini文件中,經過設置junit_suite_name的值,自定義XML文件中testsuite根節點的name信息:

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

# src/chapter-2/pytest.ini

[pytest]
junit_suite_name = pytest_chinese_doc

咱們來執行一個測試用例test_nodeid.py::test_one看看效果:

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

生成的XML測試報告:

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

咱們能夠看到,<testsuite>節點的name屬性的值,變爲咱們所指望的pytest_chinese_doc,而不是默認的pytest

JUnit XML規定time屬性應該代表測試用例執行的所有耗時,包含setupteardown中的操做,這也是pytest的默認行爲;

若是你只想記錄測試用例執行的時間,只須要作以下配置:

# src/chapter-2/pytest.ini

junit_duration_report = call

15. 在報告中爲測試用例附加額外的子節點信息

咱們有兩種方式實現這個功能:

  • 使用record_property fixture

    test_record_property用例添加一個額外的test_id

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

    在報告中的表現爲<property name="test_id" value="10010" />

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

    首先,修改pytest_collection_modifyitems鉤子方法,添加對test_id標記的支持:

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

    而後,修改測試用例:

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

    在報告中的也表現爲<property name="test_id" value="10010" />

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

    注意:

    這是咱們會接收到一個告警:

    PytestUnknownMarkWarning: Unknown pytest.mark.test_id - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html

    這是由於咱們沒有在pytest中註冊test_id標記,但不影響正常的執行;

    若是你想去除這個告警,只須要在pytest.ini的配置文件中註冊這個標記:

    [pytest]
    markers =
        test_id: 爲測試用例添加ID

注意:

變更後的報告可能不符合最新的JUnitXML的模式檢查規則,致使在某些CI工具上可能會發生未知的錯誤;

15.1. 在報告中爲測試用例附加額外的屬性信息

能夠經過record_xml_attribute fixture爲測試用例附加額外的屬性,而不像record_property爲其添加子節點;

爲測試用例添加一個test_id屬性,並修改原先的classname屬性:

# src/chapter-2/test_xml_report.py

def test_record_property2(record_xml_attribute):
    record_xml_attribute('test_id', 10010)
    record_xml_attribute('classname', 'custom_classname')
    assert 1

在報告中的表現爲<testcase classname="custom_classname" test_id="10010" ...

<!-- src/chapter-2/report/test_record_property2.xml -->

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

注意:

  • record_xml_attribute目前是一個實驗性的功能,將來可能被更強大的API所替代,但功能自己會被保留。

  • 變更後的報告可能不符合最新的JUnitXML的模式檢查規則,致使在某些CI工具上可能會發生未知的錯誤;

15.2. 在報告中爲測試集附加額外的子節點信息

4.5版本新增功能

能夠經過自定義一個session做用域級別的fixture,爲測試集添加子節點信息,而且會做用於全部的測試用例;

這個自定義的fixture須要調用另一個record_testsuite_property fixture

record_testsuite_property接收兩個參數namevalue以構成<property>標籤,其中,name必須爲字符串,value會轉換爲字符串並進行XML轉義;

# src/chapter-2/test_xml_report.py

@pytest.fixture(scope="session")
def log_global_env_facts(record_testsuite_property):
    record_testsuite_property("EXECUTOR", "luizyao")
    record_testsuite_property("LOCATION", "NJ")


def test_record_property3(log_global_env_facts):
    assert 1

生成的測試報告表現爲:在testsuite節點中,多了一個properties子節點,包含全部新增的屬性節點,並且,它和全部的testcase節點是平級的;

<!-- src/chapter-2/report/test_record_property3.xml -->

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

注意:

這樣生成的XML文件是符合最新的xunit標準的,這點和record_propertyrecord_xml_attribute正好相反;

16. 建立純文本格式的測試報告

不推薦使用,計劃在pytest 6.0中刪除這個功能

使用以下命令,能夠在指定的path中建立一個純文本的測試報告:

pytest --resultlog=path

17. 爲測試報告提供URL連接 -- pastebin服務

目前,只實現了在http://bpaste.net上的展現功能;

  • 爲每個失敗的測試用例建立一個URL

    pytest --pastebin=failed

    也能夠經過添加-x選項,只爲第一個失敗的測試用例建立一個URL;

  • 爲全部的測試用例建立一個URL

    pytest --pastebin=all

18. 儘早的加載插件

你能夠在命令行中使用-p選項,來儘早的加載某一個插件:

pytest -p mypluginmodule

-p選項接收一個name參數,這個參數能夠爲:

  • 一個完整的本地插件引入,例如:myproject.plugins,其必須是能夠import的。
  • 一個公共插件的名稱,這是其註冊時在setuptools中賦予的名字,例如:儘早的加載pytest-cov插件:
    bash pytest -p pytest_cov

19. 去使能插件

你能夠在命令行中使用-p結合no:,來去使能一個插件的加載,例如:

pytest -p no:doctest

20. python代碼中調用pytest

能夠直接在代碼中調用pytest

pytest.main()

這和你在命令行中執行pytest .幾乎是同樣的,但其也有如下特色:

  • 不會觸發SystemExit,而是返回exitcode

    # src/chapter-2/invoke_via_main.py
    
    import time
    
    
    def test_one():
        time.sleep(10)
    
    
    if __name__ == '__main__':
        import pytest
        ret = pytest.main(['-q', __file__])
        print("pytest.main() 返回 pytest.ExitCode.INTERRUPTED:", ret == pytest.ExitCode.INTERRUPTED)

    用例中有等待10秒的操做,在這期間,打斷執行(Ctr+C),pytest.main()返回的是INTERRUPTED狀態碼;

    λ pipenv run python src/chapter-2/invoke_via_main.py
    
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
    D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-2\invoke_via_main.py:26: KeyboardInterrupt
    (to show a full traceback on KeyboardInterrupt use --full-trace)
    no tests ran in 1.04s
    pytest.main() 返回 pytest.ExitCode.INTERRUPTED: True
    
    Aborted!
  • 傳遞選項和參數:

    pytest.main(["-x", "mytestdir"])
  • 指定一個插件:

    import pytest
    
    
    class MyPlugin:
        def pytest_sessionfinish(self):
            print("*** test run reporting finishing")
    
    
    pytest.main(["-qq"], plugins=[MyPlugin()])

注意:

調用pytest.main()會引入你的測試文件以及其引用的全部模塊。因爲python引入機制的緩存特性,當這些文件發生變化時,後續再調用pytest.main()(在同一個程序執行過程當中)時,並不會響應這些文件的變化。

基於這個緣由,咱們不推薦在同一個程序中屢次調用pytest.main()(例如:爲了從新執行測試;若是你確實有這個需求,或許能夠考慮pytest-repeat插件);

GitHub倉庫地址:https://github.com/luizyao/pytest-chinese-doc

相關文章
相關標籤/搜索