2齿穗、使用和調(diào)用

1. 通過python -m pytest調(diào)用pytest

你可以通過python的解釋器來執(zhí)行測試:

python -m pytest [...]

但是,這和直接執(zhí)行pytest [...]命令的效果幾乎是一模一樣的喊括;

2. pytest執(zhí)行結(jié)束時返回的狀態(tài)碼

pytest命令執(zhí)行結(jié)束胧瓜,可能會返回以下六種狀態(tài)碼:

  • 0:(OK)所有收集到的用例測試通過
  • 1:(TESTS_FAILED)有用例測試失敗
  • 2:(INTERRUPTED)用戶打斷測試執(zhí)行
  • 3:(INTERNAL_ERROR)測試執(zhí)行的過程中,發(fā)生內(nèi)部錯誤
  • 4:(USAGE_ERROR)pytest命令使用錯誤
  • 5:(NO_TESTS_COLLECTED)沒有收集到測試用例

它們在枚舉類 _pytest.main.ExitCode 中聲明郑什。并且府喳,其作為公開API的一部分,能夠直接引入和訪問:

from pytest import ExitCode

3. 獲取幫助信息

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

4. 最多允許失敗的測試用例數(shù)

當(dāng)達(dá)到最大上限時蘑拯,退出執(zhí)行劫拢;如未配置,則沒有上限:

pytest -x  # 遇到第一個失敗時强胰,退出執(zhí)行
pytest --maxfail==2  # 遇到第二個失敗時,退出執(zhí)行

5. 執(zhí)行指定的測試用例

pytest支持多種方式來執(zhí)行特定的測試用例:

5.1. 執(zhí)行指定模塊中的測試用例

pytest test_mod.py

5.2. 執(zhí)行指定目錄下所有的測試用例

pytest testing/

5.3. 執(zhí)行文件名妹沙、類名或者函數(shù)名中包含特定關(guān)鍵字的測試用例

執(zhí)行當(dāng)前目錄下偶洋,名字包含_class但不包含two的測試用例:

pytest -k "_class and not two" .

注意:python的關(guān)鍵字不可以應(yīng)用在-k選項中,例如距糖,class玄窝、def等。

5.4. 執(zhí)行指定nodeid的測試用例

pytest為每一個收集到的測試用例指定一個唯一的nodeid悍引。其由模塊名加說明符構(gòu)成恩脂,中間以::間隔。

其中趣斤,說明符可以是類名俩块、函數(shù)名以及由parametrize標(biāo)記賦予的參數(shù)

# 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

在上述示例中,我們創(chuàng)建了三個測試用例浓领,分別對應(yīng)不同的說明符

  • 指定函數(shù)名執(zhí)行

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

      $ pipenv run pytest -q -s src/chapter-2/test_nodeid.py::TestNodeId::test_one
      TestNodeId::test_one
      .
      1 passed in 0.01s
    
  • 指定parametrize標(biāo)記賦予的參數(shù)執(zhí)行

      $ 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
    

    這里對參數(shù)x玉凯、y賦值的形式是[1-1],中間以-間隔联贩;

    單個或多個參數(shù)的賦值形式以此類比漫仆;并且,只能為[1-1]或者[3-4]泪幌,其它的會報錯盲厌;

5.5. 執(zhí)行指定標(biāo)記的用例

pytest -m slow

5.6. 執(zhí)行指定包中的測試用例

pytest --pyargs pkg.testing

pytest會引入pkg.testing包,并在它的系統(tǒng)目錄下搜尋測試用例并執(zhí)行祸泪;

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

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

pytest -l, --showlocals         # 打印本地變量
pytest --tb=auto                # 默認(rèn)模式
pytest --tb=long                # 盡可能詳細(xì)的輸出
pytest --tb=short               # 更簡短的輸出
pytest --tb=line                # 每個失敗信息總結(jié)在一行中
pytest --tb=native              # python的標(biāo)準(zhǔn)輸出
pytest --tb=no                  # 不打印失敗信息

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

7. 總結(jié)報告

-r選項可以在執(zhí)行結(jié)束后,打印一個簡短的總結(jié)報告升略。在執(zhí)行的測試用例很多時微王,可以讓你對結(jié)果有個清晰的了解:

# 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選項后面要緊接這一個參數(shù)屡限,用于過濾顯示測試用例的結(jié)果。

以下是所有有效的字符參數(shù):

  • f:失敗的
  • E:出錯的
  • s:跳過執(zhí)行的
  • x:跳過執(zhí)行炕倘,并標(biāo)記為xfailed的
  • X:跳過執(zhí)行钧大,并標(biāo)記為xpassed的
  • p:測試通過的
  • P:測試通過,并且有輸出信息的罩旋;即用例中有print
  • a:除了測試通過的啊央,其他所有的;即除了pP
  • A:所有的

上述字符參數(shù)可以疊加使用涨醋,例如:我們期望過濾出失敗的和未執(zhí)行的:

pytest -rfs

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

PDBpython內(nèi)建的診斷器瓜饥,pytest允許通過以下命令在執(zhí)行失敗時進(jìn)入這個診斷器模式:

pytest --pdb

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

# 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變量中,你可以在交互環(huán)境中訪問它們溯警;

使用exit命令趣苏,退出PDB環(huán)境;

9. 開始執(zhí)行時就加載PDB環(huán)境

通過以下命令梯轻,pytest允許你在每個測試用例開始執(zhí)行時食磕,就加載PDB環(huán)境:

pytest --trace

10. 設(shè)置斷點

在測試用例代碼中添加import pdb;pdb.set_trace(),當(dāng)其被調(diào)用時喳挑,pytest會停止這條用例的輸出:

  • 其他用例不受影響彬伦;
  • 通過continue命令,退出PDB環(huán)境伊诵,并繼續(xù)執(zhí)行用例媚朦;

11. 使用內(nèi)置的中斷函數(shù)

python 3.7介紹了一個內(nèi)置breakpoint()函數(shù)。pytest可以在以下場景中支持使用:

  • 當(dāng)breakpoint()被調(diào)用日戈,并且PYTHONBREAKPOINTNone時询张,pytest會使用內(nèi)部自定義的PDB代替系統(tǒng)的;
  • 測試執(zhí)行結(jié)束時浙炼,自動切回系統(tǒng)自帶的PDB份氧;
  • 當(dāng)加上--pdb選項時,breakpoint()和測試發(fā)生錯誤時弯屈,都會調(diào)用內(nèi)部自定義的PDB蜗帜;
  • --pdbcls選項允許指定一個用戶自定義的PDB類;

12. 分析測試執(zhí)行時間

獲取執(zhí)行最慢的10個測試用例:

pytest --durations=10

默認(rèn)情況下资厉,pytest不會顯示執(zhí)行時間<0.01s的測試用例厅缺,可以使用-vv選項查看它們;

13. 錯誤句柄

5.0版本新增特性

在測試執(zhí)行中發(fā)生段錯誤或者超時的情況下,faulthandler標(biāo)準(zhǔn)模塊可以轉(zhuǎn)儲python的回溯信息湘捎;

它在pytest的執(zhí)行中默認(rèn)使能诀豁,使用-p no:faulthandler選項可以關(guān)閉它;

同樣窥妇,faulthandler_timeout=X配置項舷胜,可用于當(dāng)測試用例的完成時間超過X秒時,轉(zhuǎn)儲所有線程的python回溯信息:

# src/chapter-2/pytest.ini

[pytest]
faulthandler_timeout=5

配置測試執(zhí)行的超時時間是5秒活翩;

# test_fault_handler.py 

import time


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

測試用例中添加等待7秒的操作烹骨;

  • 默認(rèn)使能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
    

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

  • 去使能faulthandler的情況:

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

    超時并不會觸發(fā)回溯信息的打泳诨馈;

注意:

這個功能是從pytest-faulthandler插件合并而來的拉宗,但是有兩點不同:

  • 去使能時遇汞,使用-p no:faulthandler代替原來的--no-faulthandler;
  • 使用faulthandler_timeout配置項代替--faulthandler-timeout命令行選項來配置超時時間。當(dāng)然簿废,你也可以使用-o faulthandler_timeout=X在命令行配置;

14. 創(chuàng)建JUnitXML格式的測試報告

使用如下命令络它,可以在指定的path中創(chuàng)建一個能被Jenkins或者其他CI工具讀取的XML格式的測試報告:

pytest --junitxml=path

你可以在項目的pytest.ini文件中族檬,通過設(shè)置junit_suite_name的值,自定義XML文件中testsuite根節(jié)點的name信息:

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

# src/chapter-2/pytest.ini

[pytest]
junit_suite_name = pytest_chinese_doc

我們來執(zhí)行一個測試用例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>節(jié)點的name屬性的值,變?yōu)槲覀兯谕?code>pytest_chinese_doc点楼,而不是默認(rèn)的pytest扫尖;

JUnit XML規(guī)定time屬性應(yīng)該表明測試用例執(zhí)行的全部耗時,包含setupteardown中的操作掠廓,這也是pytest的默認(rèn)行為换怖;

如果你只想記錄測試用例執(zhí)行的時間,只需要做如下配置:

# src/chapter-2/pytest.ini

junit_duration_report = call

14.1. 在報告中為測試用例附加額外的子節(jié)點信息

我們有兩種方式實現(xiàn)這個功能:

  • 使用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
    

    在報告中的表現(xiàn)為<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>
    
  • 解析一個自定義的標(biāo)記@pytest.mark.test_id():

    首先蟀瞧,修改pytest_collection_modifyitems鉤子方法沉颂,添加對test_id標(biāo)記的支持:

      # 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
    

    在報告中的也表現(xiàn)為<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標(biāo)記悦污,但不影響正常的執(zhí)行铸屉;

    如果你想去除這個告警,只需要在pytest.ini的配置文件中注冊這個標(biāo)記:

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

注意:

變動后的報告可能不符合最新的JUnitXML的模式檢查規(guī)則切端,導(dǎo)致在某些CI工具上可能會發(fā)生未知的錯誤彻坛;

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

可以通過record_xml_attribute fixture為測試用例附加額外的屬性,而不像record_property為其添加子節(jié)點;

為測試用例添加一個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

在報告中的表現(xiàn)為<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的模式檢查規(guī)則仪搔,導(dǎo)致在某些CI工具上可能會發(fā)生未知的錯誤;

14.3. 在報告中為測試集附加額外的子節(jié)點信息

4.5版本新增功能

可以通過自定義一個session作用域級別的fixture蜻牢,為測試集添加子節(jié)點信息烤咧,并且會作用于所有的測試用例;

這個自定義的fixture需要調(diào)用另外一個record_testsuite_property fixture

record_testsuite_property接收兩個參數(shù)namevalue以構(gòu)成<property>標(biāo)簽抢呆,其中煮嫌,name必須為字符串,value會轉(zhuǎn)換為字符串并進(jìn)行XML轉(zhuǎn)義抱虐;

# 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

生成的測試報告表現(xiàn)為:在testsuite節(jié)點中昌阿,多了一個properties子節(jié)點,包含所有新增的屬性節(jié)點恳邀,而且懦冰,它和所有的testcase節(jié)點是平級的;

<!-- 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標(biāo)準(zhǔn)的谣沸,這點和record_property刷钢、record_xml_attribute正好相反;

15. 創(chuàng)建純文本格式的測試報告

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

使用如下命令内地,可以在指定的path中創(chuàng)建一個純文本的測試報告:

pytest --resultlog=path

16. 為測試報告提供URL鏈接 -- pastebin服務(wù)

目前,只實現(xiàn)了在http://bpaste.net上的展示功能赋除;

  • 為每一個失敗的測試用例創(chuàng)建一個URL

      pytest --pastebin=failed
    

    也可以通過添加-x選項阱缓,只為第一個失敗的測試用例創(chuàng)建一個URL;

  • 為所有的測試用例創(chuàng)建一個URL

      pytest --pastebin=all
    

17. 盡早的加載插件

你可以在命令行中使用-p選項举农,來盡早的加載某一個插件:

pytest -p mypluginmodule

-p選項接收一個name參數(shù)荆针,這個參數(shù)可以為:

  • 一個完整的本地插件引入,例如:myproject.plugins颁糟,其必須是可以import的祭犯。

  • 一個公共插件的名稱,這是其注冊時在setuptools中賦予的名字滚停,例如:盡早的加載pytest-cov插件:

    pytest -p pytest_cov
    

18. 去使能插件

你可以在命令行中使用-p結(jié)合no:沃粗,來去使能一個插件的加載,例如:

pytest -p no:doctest

19. python代碼中調(diào)用pytest

可以直接在代碼中調(diào)用pytest

pytest.main()

這和你在命令行中執(zhí)行pytest .幾乎是一樣的键畴,但其也有以下特點:

  • 不會觸發(fā)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秒的操作突雪,在這期間,打斷執(zhí)行(Ctr+C)涡贱,pytest.main()返回的是INTERRUPTED狀態(tài)碼咏删;

      λ 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!
    
  • 傳遞選項和參數(shù):

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

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

注意:

調(diào)用pytest.main()會引入你的測試文件以及其引用的所有模塊。由于python引入機制的緩存特性问词,當(dāng)這些文件發(fā)生變化時督函,后續(xù)再調(diào)用pytest.main()(在同一個程序執(zhí)行過程中)時,并不會響應(yīng)這些文件的變化激挪。

基于這個原因辰狡,我們不推薦在同一個程序中多次調(diào)用pytest.main()(例如:為了重新執(zhí)行測試;如果你確實有這個需求垄分,或許可以考慮pytest-repeat插件)宛篇;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市薄湿,隨后出現(xiàn)的幾起案子叫倍,更是在濱河造成了極大的恐慌,老刑警劉巖豺瘤,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吆倦,死亡現(xiàn)場離奇詭異,居然都是意外死亡坐求,警方通過查閱死者的電腦和手機蚕泽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞻赶,“玉大人,你說我怎么就攤上這事派任≡已罚” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵掌逛,是天一觀的道長师逸。 經(jīng)常有香客問我,道長豆混,這世上最難降的妖魔是什么篓像? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮皿伺,結(jié)果婚禮上员辩,老公的妹妹穿的比我還像新娘。我一直安慰自己鸵鸥,他們只是感情好奠滑,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布丹皱。 她就那樣靜靜地躺著,像睡著了一般宋税。 火紅的嫁衣襯著肌膚如雪摊崭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天杰赛,我揣著相機與錄音呢簸,去河邊找鬼。 笑死乏屯,一個胖子當(dāng)著我的面吹牛根时,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓶珊,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼啸箫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伞芹?” 一聲冷哼從身側(cè)響起忘苛,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唱较,沒想到半個月后扎唾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡南缓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年胸遇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汉形。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡纸镊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出概疆,到底是詐尸還是另有隱情逗威,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布岔冀,位于F島的核電站凯旭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏使套。R本人自食惡果不足惜罐呼,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一启盛、第九天 我趴在偏房一處隱蔽的房頂上張望奉狈。 院中可真熱鬧,春花似錦武通、人聲如沸奉呛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至危尿,卻和暖如春呐萌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谊娇。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工肺孤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人济欢。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓赠堵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親法褥。 傳聞我的和親對象是個殘疾皇子茫叭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容