Spec-Zone.ru › pytest

Управление выводом pytest

Изменение вывода трассировки Python

Примеры изменения вывода трассировки:

pytest --showlocals     # show local variables in tracebacks
pytest -l               # show local variables (shortcut)
pytest --no-showlocals  # hide local variables (if addopts enables them)

pytest --capture=fd  # default, capture at the file descriptor level
pytest --capture=sys # capture at the sys level
pytest --capture=no  # don't capture
pytest -s            # don't capture (shortcut)
pytest --capture=tee-sys # capture to logs but also output to sys level streams

pytest --tb=auto    # (default) 'long' tracebacks for the first and last
                     # entry, but 'short' style for the other entries
pytest --tb=long    # exhaustive, informative traceback formatting
pytest --tb=short   # shorter traceback format
pytest --tb=line    # only one line per failure
pytest --tb=native  # Python standard library formatting
pytest --tb=no      # no traceback at all

Параметр --full-trace заставляет выводить очень длинные трассировки при ошибке (длиннее, чем --tb=long). Он также гарантирует вывод трассировки стека при KeyboardInterrupt (Ctrl+C). Это очень полезно, если тесты выполняются слишком долго и вы прерываете их с помощью Ctrl+C, чтобы выяснить, где тесты зависли. По умолчанию вывод не отображается (поскольку pytest перехватывает KeyboardInterrupt). Используя этот параметр, вы гарантируете, что трассировка будет показана.

Подробность вывода

Примеры изменения подробности вывода:

pytest --quiet          # quiet - less verbose - mode
pytest -q               # quiet - less verbose - mode (shortcut)
pytest -v               # increase verbosity, display individual test names
pytest -vv              # more verbose, display more details from the test output
pytest -vvv             # not a standard , but may be used for even more detail in certain setups

Флаг -v управляет подробностью вывода pytest в разных аспектах: ходом выполнения сеанса тестирования, сведениями об утверждениях при сбое тестов, сведениями о фикстурах с помощью --fixtures и т. д.

Рассмотрим этот простой файл:

# content of test_verbosity_example.py
def test_ok():
    pass


def test_words_fail():
    fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
    fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
    assert fruits1 == fruits2


def test_numbers_fail():
    number_to_text1 = {str(x): x for x in range(5)}
    number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
    assert number_to_text1 == number_to_text2


def test_long_text_fail():
    long_text = "Lorem ipsum dolor sit amet " * 10
    assert "hello world" in long_text

Обычный запуск pytest выводит следующее (мы пропускаем заголовок, чтобы сосредоточиться на остальном):

$ pytest --no-header
=========================== test session starts ============================
collected 4 items

test_verbosity_example.py .FFF                                       [100%]

================================= FAILURES =================================
_____________________________ test_words_fail ______________________________

    def test_words_fail():
        fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
        fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
>       assert fruits1 == fruits2
E       AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
E
E         At index 2 diff: 'grapes' != 'orange'
E         Use -v to get more diff

test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________

    def test_numbers_fail():
        number_to_text1 = {str(x): x for x in range(5)}
        number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
>       assert number_to_text1 == number_to_text2
E       AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
E
E         Omitting 1 identical items, use -vv to show
E         Left contains 4 more items:
E         {'1': 1, '2': 2, '3': 3, '4': 4}
E         Right contains 4 more items:
E         {'10': 10, '20': 20, '30': 30, '40': 40}
E         Use -v to get more diff

test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________

    def test_long_text_fail():
        long_text = "Lorem ipsum dolor sit amet " * 10
>       assert "hello world" in long_text
E       AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ips... sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '

test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
======================= 3 failed, 1 passed in 0.12s ========================

Обратите внимание:

  • Каждый тест в файле обозначается в выводе одним символом: . для успешного прохождения, F для сбоя.
  • test_words_fail завершился с ошибкой, и мы видим краткую сводку, указывающую, что элементы с индексом 2 в двух списках различаются.
  • test_numbers_fail завершился с ошибкой, и мы видим сводку различий между левыми и правыми элементами словаря. Совпадающие элементы опущены.
  • test_long_text_fail завершился с ошибкой, а правая часть выражения in усечена с помощью ...`, поскольку она превышает внутренний порог (сейчас — 240 символов).

Теперь можно увеличить подробность вывода pytest:

$ pytest --no-header -v
=========================== test session starts ============================
collecting ... collected 4 items

test_verbosity_example.py::test_ok PASSED                            [ 25%]
test_verbosity_example.py::test_words_fail FAILED                    [ 50%]
test_verbosity_example.py::test_numbers_fail FAILED                  [ 75%]
test_verbosity_example.py::test_long_text_fail FAILED                [100%]

================================= FAILURES =================================
_____________________________ test_words_fail ______________________________

    def test_words_fail():
        fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
        fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
>       assert fruits1 == fruits2
E       AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
E
E         At index 2 diff: 'grapes' != 'orange'
E
E         Full diff:
E           [
E               'banana',
E               'apple',...
E
E         ...Full output truncated (7 lines hidden), use '-vv' to show

test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________

    def test_numbers_fail():
        number_to_text1 = {str(x): x for x in range(5)}
        number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
>       assert number_to_text1 == number_to_text2
E       AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
E
E         Omitting 1 identical items, use -vv to show
E         Left contains 4 more items:
E         {'1': 1, '2': 2, '3': 3, '4': 4}
E         Right contains 4 more items:
E         {'10': 10, '20': 20, '30': 30, '40': 40}
E         ...
E
E         ...Full output truncated (16 lines hidden), use '-vv' to show

test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________

    def test_long_text_fail():
        long_text = "Lorem ipsum dolor sit amet " * 10
>       assert "hello world" in long_text
E       AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '

test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
======================= 3 failed, 1 passed in 0.12s ========================

Теперь обратите внимание:

  • Каждый тест в файле занимает в выводе отдельную строку.
  • test_words_fail теперь полностью показывает оба списка, в которых произошёл сбой, а также индекс различающегося элемента.
  • test_numbers_fail теперь показывает текстовое сравнение двух словарей, но оно усечено.
  • test_long_text_fail больше не усекает правую часть выражения in, поскольку внутренний порог усечения теперь выше (сейчас — 2400 символов).

Теперь увеличим подробность вывода ещё сильнее:

$ pytest --no-header -vv
=========================== test session starts ============================
collecting ... collected 4 items

test_verbosity_example.py::test_ok PASSED                            [ 25%]
test_verbosity_example.py::test_words_fail FAILED                    [ 50%]
test_verbosity_example.py::test_numbers_fail FAILED                  [ 75%]
test_verbosity_example.py::test_long_text_fail FAILED                [100%]

================================= FAILURES =================================
_____________________________ test_words_fail ______________________________

    def test_words_fail():
        fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
        fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
>       assert fruits1 == fruits2
E       AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
E
E         At index 2 diff: 'grapes' != 'orange'
E
E         Full diff:
E           [
E               'banana',
E               'apple',
E         -     'orange',
E         ?      ^  ^^
E         +     'grapes',
E         ?      ^  ^ +
E               'melon',
E               'kiwi',
E           ]

test_verbosity_example.py:8: AssertionError
____________________________ test_numbers_fail _____________________________

    def test_numbers_fail():
        number_to_text1 = {str(x): x for x in range(5)}
        number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
>       assert number_to_text1 == number_to_text2
E       AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
E
E         Common items:
E         {'0': 0}
E         Left contains 4 more items:
E         {'1': 1, '2': 2, '3': 3, '4': 4}
E         Right contains 4 more items:
E         {'10': 10, '20': 20, '30': 30, '40': 40}
E
E         Full diff:
E           {
E               '0': 0,
E         -     '10': 10,
E         ?       -    -
E         +     '1': 1,
E         -     '20': 20,
E         ?       -    -
E         +     '2': 2,
E         -     '30': 30,
E         ?       -    -
E         +     '3': 3,
E         -     '40': 40,
E         ?       -    -
E         +     '4': 4,
E           }

test_verbosity_example.py:14: AssertionError
___________________________ test_long_text_fail ____________________________

    def test_long_text_fail():
        long_text = "Lorem ipsum dolor sit amet " * 10
>       assert "hello world" in long_text
E       AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '

test_verbosity_example.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_verbosity_example.py::test_words_fail - AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']

  At index 2 diff: 'grapes' != 'orange'

  Full diff:
    [
        'banana',
        'apple',
  -     'orange',
  ?      ^  ^^
  +     'grapes',
  ?      ^  ^ +
        'melon',
        'kiwi',
    ]
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}

  Common items:
  {'0': 0}
  Left contains 4 more items:
  {'1': 1, '2': 2, '3': 3, '4': 4}
  Right contains 4 more items:
  {'10': 10, '20': 20, '30': 30, '40': 40}

  Full diff:
    {
        '0': 0,
  -     '10': 10,
  ?       -    -
  +     '1': 1,
  -     '20': 20,
  ?       -    -
  +     '2': 2,
  -     '30': 30,
  ?       -    -
  +     '3': 3,
  -     '40': 40,
  ?       -    -
  +     '4': 4,
    }
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
======================= 3 failed, 1 passed in 0.12s ========================

Теперь обратите внимание:

  • Каждый тест в файле занимает в выводе отдельную строку.
  • test_words_fail в этом случае выводит то же, что и раньше.
  • test_numbers_fail теперь показывает полное текстовое сравнение двух словарей.
  • test_long_text_fail, как и прежде, не усекает правую часть, но теперь pytest вообще не будет усекать текст независимо от его размера.

Это были примеры того, как подробность вывода влияет на обычный вывод сеанса тестирования. Она также используется в других ситуациях: например, при использовании pytest --fixtures -v отображаются даже фикстуры, имена которых начинаются с _.

Поддерживаются более высокие уровни подробности вывода (-vvv, -vvvv и т. д.), но в данный момент они не влияют на сам pytest; однако некоторые плагины могут их использовать.

Тонкая настройка подробности вывода

Помимо задания общего уровня подробности для приложения, можно независимо управлять отдельными аспектами. Для этого в файле конфигурации задаётся уровень подробности для конкретного аспекта вывода.

verbosity_assertions: управляет подробностью вывода утверждений при запуске pytest. Запуск pytest --no-header со значением 2 дал бы такой же вывод, как в предыдущем примере, но каждый тест в файле был бы обозначен одним символом.

assertion_text_diff_style: управляет отображением pytest сбоев str == str.

  • ndiff (значение по умолчанию) выводит различия с помощью встроенных маркеров сравнения.
  • block выводит сравнения строк в виде отдельных блоков Left: и Right:, которые могут быть удобнее для чтения, если различия в пробелах или отступах затрудняют восприятие.

Обратите внимание, что этот параметр, как и любой другой параметр конфигурации, можно задать непосредственно в командной строке с помощью -o assertion_text_diff_style=block.

verbosity_test_cases: управляет подробностью вывода при выполнении тестов pytest. Запуск pytest --no-header со значением 2 дал бы такой же вывод, как в первом примере с настройкой подробности, но каждый тест в файле занимал бы отдельную строку.

Создание подробного итогового отчёта

Флаг -r позволяет вывести в конце сеанса тестирования «краткую сводку результатов тестов», благодаря чему в больших наборах тестов проще получить чёткое представление обо всех сбоях, пропусках, ожидаемых сбоях и т. д.

По умолчанию используется fE, чтобы перечислять сбои и ошибки.

Пример:

# content of test_example.py
import pytest


@pytest.fixture
def error_fixture():
    assert 0


def test_ok():
    print("ok")


def test_fail():
    assert 0


def test_error(error_fixture):
    pass


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


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


@pytest.mark.xfail(reason="always xfail")
def test_xpass():
    pass
$ pytest -ra
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 6 items

test_example.py .FEsxX                                               [100%]

================================== ERRORS ==================================
_______________________ ERROR at setup of test_error _______________________

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

test_example.py:6: AssertionError
================================= FAILURES =================================
________________________________ test_fail _________________________________

    def test_fail():
>       assert 0
E       assert 0

test_example.py:14: AssertionError
================================= XPASSES ==================================
========================= short test summary info ==========================
SKIPPED [1] test_example.py:22: skipping this test
XFAIL test_example.py::test_xfail - 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.12s ===

После параметра -r можно указать несколько символов; a, использованный выше, означает «всё, кроме успешных тестов».

Вот полный список доступных символов:

  • f — тест завершился с ошибкой
  • E — ошибка
  • s — пропущен
  • x — ожидаемый сбой
  • X — неожиданный успех
  • p — успешно пройден
  • P — успешно пройден с выводом

Специальные символы для выбора или исключения групп:

  • a — всё, кроме pP
  • A — всё
  • N — ничего; это можно использовать, чтобы ничего не выводить (поскольку по умолчанию используется fE)

Можно использовать несколько символов. Например, чтобы видеть только тесты, завершившиеся с ошибкой, и пропущенные тесты, выполните команду:

$ pytest -rfs
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 6 items

test_example.py .FEsxX                                               [100%]

================================== ERRORS ==================================
_______________________ ERROR at setup of test_error _______________________

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

test_example.py:6: AssertionError
================================= FAILURES =================================
________________________________ test_fail _________________________________

    def test_fail():
>       assert 0
E       assert 0

test_example.py:14: AssertionError
========================= short test summary info ==========================
FAILED test_example.py::test_fail - assert 0
SKIPPED [1] test_example.py:22: skipping this test
== 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===

Параметр p перечисляет успешно пройденные тесты, а P добавляет раздел «УСПЕШНО ПРОЙДЕНЫ» с тестами, которые прошли успешно, но для которых был перехвачен вывод:

$ pytest -rpP
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-9.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 6 items

test_example.py .FEsxX                                               [100%]

================================== ERRORS ==================================
_______________________ ERROR at setup of test_error _______________________

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

test_example.py:6: AssertionError
================================= FAILURES =================================
________________________________ test_fail _________________________________

    def test_fail():
>       assert 0
E       assert 0

test_example.py:14: 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.12s ===

Примечание

По умолчанию варианты пропущенных параметризованных тестов объединяются, если у них совпадает причина пропуска. Чтобы выводить каждый пропущенный тест отдельно, используйте --no-fold-skipped.

Изменение ограничений на усечение

По умолчанию вывод усекается до 8 строк или 640 символов — в зависимости от того, что наступит раньше. Чтобы задать собственные ограничения на усечение, можно использовать следующие параметры файла конфигурации:

toml

[pytest]
truncation_limit_lines = 10
truncation_limit_chars = 90

ini

[pytest]
truncation_limit_lines = 10
truncation_limit_chars = 90

Это заставит pytest усекать утверждения до 10 строк или 90 символов — в зависимости от того, что наступит раньше.

Если задать для truncation_limit_lines и truncation_limit_chars значение 0, усечение будет отключено. Однако если задать только одно из этих значений, будет отключён лишь один режим усечения, а другой останется включённым.

Создание файлов в формате JUnitXML

Чтобы создавать файлы с результатами, которые можно прочитать с помощью Jenkins или других серверов непрерывной интеграции, используйте следующую команду:

pytest --junit-xml=path

чтобы создать XML-файл по адресу path.

Чтобы задать имя корневого элемента набора тестов в XML, настройте параметр junit_suite_name в файле конфигурации:

toml

[pytest]
junit_suite_name = "my_suite"

ini

[pytest]
junit_suite_name = my_suite

Добавлено в версии 4.0.

Судя по спецификации JUnit XML, атрибут "time" должен содержать общее время выполнения тестов, включая настройку и очистку (1, 2). Это поведение pytest по умолчанию. Чтобы выводить только длительность вызовов, настройте параметр junit_duration_report следующим образом:

toml

[pytest]
junit_duration_report = "call"

ini

[pytest]
junit_duration_report = call

record_property

Чтобы записать дополнительную информацию о тесте, можно использовать фикстуру record_property:

def test_function(record_property):
    record_property("example_key", 1)
    assert True

Это добавит дополнительное свойство example_key="1" в тег testcase, созданный в результате:

<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
  <properties>
    <property name="example_key" value="1" />
  </properties>
</testcase>

Эту функциональность также можно использовать вместе с пользовательскими маркерами:

# content of 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))

А в тестах:

# content of test_function.py
import pytest


@pytest.mark.test_id(1501)
def test_function():
    assert True

В результате получится:

<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
  <properties>
    <property name="test_id" value="1501" />
  </properties>
</testcase>

Предупреждение

Обратите внимание, что использование этой функции нарушит проверку по последней схеме JUnitXML. Это может стать проблемой при использовании некоторых серверов CI.

record_xml_attribute

Чтобы добавить дополнительный атрибут XML к элементу testcase, можно использовать фикстуру record_xml_attribute. С её помощью также можно переопределить существующие значения:

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, эта фикстура не добавляет новый дочерний элемент. Вместо этого она добавляет атрибут assertions="REQ-1234" в тег testcase и заменяет значение по умолчанию classname на "classname=custom_classname":

<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 — экспериментальная функция, и в будущих версиях её интерфейс может быть заменён на более мощный и универсальный. Сама функциональность при этом сохранится.

Использование этой функции вместо record_xml_property может помочь при разборе XML-отчёта инструментами CI. Однако некоторые анализаторы строго ограничивают допустимые элементы и атрибуты. Многие инструменты проверяют входящие XML-файлы по схеме XSD (например, по приведённой ниже). Убедитесь, что используемые имена атрибутов допускаются вашим анализатором.

Ниже приведена схема, используемая Jenkins для проверки XML-отчёта:

<xs:element name="testcase">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
            <xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="name" type="xs:string" use="required"/>
        <xs:attribute name="assertions" type="xs:string" use="optional"/>
        <xs:attribute name="time" type="xs:string" use="optional"/>
        <xs:attribute name="classname" type="xs:string" use="optional"/>
        <xs:attribute name="status" type="xs:string" use="optional"/>
    </xs:complexType>
</xs:element>

Предупреждение

Обратите внимание, что использование этой функции нарушит проверку по последней схеме JUnitXML. Это может стать проблемой при использовании некоторых серверов CI.

record_testsuite_property

Добавлено в версии 4.5.

Чтобы добавить узел properties на уровне набора тестов, содержащий свойства, относящиеся ко всем тестам, можно использовать фикстуру record_testsuite_property с областью видимости session:

Фикстуру record_testsuite_property с областью видимости session можно использовать для добавления свойств, относящихся ко всем тестам.

import pytest


@pytest.fixture(scope="session", autouse=True)
def log_global_env_facts(record_testsuite_property):
    record_testsuite_property("ARCH", "PPC")
    record_testsuite_property("STORAGE_TYPE", "CEPH")


class TestMe:
    def test_foo(self):
        assert True

Фикстура представляет собой вызываемый объект, принимающий name и value тега <property>, добавляемого на уровне набора тестов в создаваемом XML:

<testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006">
  <properties>
    <property name="ARCH" value="PPC"/>
    <property name="STORAGE_TYPE" value="CEPH"/>
  </properties>
  <testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
</testsuite>

name должен быть строкой, а value будет преобразован в строку и корректно экранирован для XML.

Создаваемый XML совместим с последним стандартом xunit, в отличие от record_property и record_xml_attribute.

Отправка отчёта о тестировании в онлайн-сервис Pastebin

Создание URL для каждого сбоя теста:

pytest --pastebin=failed

Это отправит сведения о запуске тестов в удалённый сервис Paste и предоставит URL для каждого сбоя. Вы можете выбирать тесты как обычно или, например, добавить -x, если хотите отправить только один конкретный сбой.

Создание URL для журнала всего сеанса тестирования:

pytest --pastebin=all

В настоящее время реализована отправка только в сервис https://bpaste.net/.

Изменено в версии 5.2.

Если по какой-либо причине не удаётся создать URL, вместо прерывания всего набора тестов выводится предупреждение.

© 2015–2026 Holger Krekel and pytest-dev team
Licensed under the MIT License.
https://docs.pytest.org/en/stable/how-to/output.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API