Добавить стандартный вывод подпроцесса в отчет JSON, если тестовый пример не прошел - PullRequest
1 голос
/ 06 мая 2020

Я исследую способы добавления к отчету JSON, сгенерированному pytest-json или pytest-json-report: я не зацикливаюсь ни на одном из плагинов. До сих пор я проводил большую часть своей оценки с использованием pytest-json. Так, например, объект JSON имеет это для тестового примера

{
    "name": "fixture_test.py::test_failure1",
    "duration": 0.0012421607971191406,
    "run_index": 2,
    "setup": {
        "name": "setup",
        "duration": 0.00011181831359863281,
        "outcome": "passed"
    },
    "call": {
        "name": "call",
        "duration": 0.0008759498596191406,
        "outcome": "failed",
        "longrepr": "def test_failure1():\n>       assert 3 == 4, \"3 always equals 3\"\nE       AssertionError: 3 always equals 3\nE       assert 3 == 4\n\nfixture_test.py:19: AssertionError"
    },
    "teardown": {
        "name": "teardown",
        "duration": 0.00014257431030273438,
        "outcome": "passed"
    },
    "outcome": "failed"
}

Это из экспериментов, которые я пытаюсь. На практике некоторые из тестовых примеров выполняются путем создания подпроцесса через Popen, и утверждение состоит в том, что определенная строка появляется в stdout. В случае сбоя тестового примера мне нужно добавить ключ / значение в словарь call , который содержит стандартный вывод этого подпроцесса. До сих пор я тщетно пытался найти правильное приспособление или устройство для выполнения sh этого. Кажется, что pytest_exception_interact может быть путем к go, но углубление в структуру JSON до сих пор ускользало от меня. Все, что мне нужно сделать, это добавить / изменить структуру JSON в момент возникновения ошибки. Кажется, что pytest_runtest_call слишком тяжеловесен.

В качестве альтернативы, есть ли способ изменить значение longrepr в приведенном выше? Я не смог найти правильный способ сделать что-либо из этого, и пришло время спросить.

1 Ответ

1 голос
/ 06 мая 2020

Похоже, что проект pytest-json уже не существует. Разработчик / владелец pytest-json-report может это сказать (в разделе Сопутствующие инструменты по этой ссылке ).

pytest- json имеет несколько замечательных функций, но, похоже, не поддерживается . Я позаимствовал оттуда некоторые идеи и тестовые примеры.

Проект pytest-json-report обрабатывает именно тот случай, который мне нужен: захват стандартного вывода из подпроцесса и его размещение в отчете JSON. Ниже приведен грубый пример этого:

import subprocess as sp
import pytest
import sys
import re

def specialAssertHandler(str, assertMessage):
    # because pytest automatically captures stdout,stderr this is all that's needed
    # when the report is generated, this will be in a field named "stdout"
    print(str)
    return assertMessage

def test_subProcessStdoutCapture():
    # NOTE: if you're version of Python 3 is sufficiently mature, add text=True also
    proc = sp.Popen(['find', '.', '-name', '*.json'], stdout=sp.PIPE)

    # NOTE: I had this because on the Ubuntu I was using, this is the version of
    # Python and the return of proc.stdout.read() is a binary object not a string
    if sys.version[0] == 3 and sys.version[6]:
        output = proc.stdout.read().decode()
    elif sys.version[0] == 2:
        # The other version of Python I'm using is 2.7.15, it's exceedingly frustrating
        # that the Python language def changed so between 2 and 3.  In 2, the output
        # was already a string object
        output = proc.stdout.read()

    m = re.search('some string', output)
    assert m is not None, specialAssertHandler(output, "did not find 'some string' in output")

При использовании вышеупомянутого pytest-json-report полный вывод подпроцесса фиксируется инфраструктурой и помещается в вышеупомянутый отчет. Отрывок, показывающий это, приведен ниже:

        {
            "nodeid": "expirment_test.py::test_stdout",
            "lineno": 25,
            "outcome": "failed",
            "keywords": [
                "PyTest",
                "test_stdout",
                "expirment_test.py"
            ],
            "setup": {
                "duration": 0.0002694129943847656,
                "outcome": "passed"
            },
            "call": {
                "duration": 0.02718186378479004,
                "outcome": "failed",
                "crash": {
                    "path": "/home/afalanga/devel/PyTest/expirment_test.py",
                    "lineno": 32,
                    "message": "AssertionError: Expected to find always\nassert None is not None"
                },
                "traceback": [
                    {
                        "path": "expirment_test.py",
                        "lineno": 32,
                        "message": "AssertionError"
                    }
                ],
                "stdout": "./.report.json\n./report.json\n./report1.json\n./report2.json\n./simple_test.json\n./testing_addition.json\n\n",
                "longrepr": "..."
            },
            "teardown": {
                "duration": 0.0004875659942626953,
                "outcome": "passed"
            }
        }

Поле longrepr содержит полный текст тестового примера, но в интересах краткости оно состоит из многоточия. В поле crash помещается значение assertMessage из моего примера. Это показывает, что можно помещать такие сообщения в отчет в момент возникновения, а не после обработки.

Я думаю, что можно «умно» справиться с этим, используя ловушку, на которую я ссылался в моем исходном вопросе pytest_exception_interact. Если это так, я обновлю этот ответ демонстрацией.

...