Написание простого хука Pytest
Pytest — это моя среда тестирования Python из-за ее гибкости. Одной из его замечательных функций является возможность записывать хуки в различные моменты выполнения набора тестов. Все они могут быть ссылками через Документация по API. На работе у нас могут быть сотни тестов, и иногда изменения могут привести к сбою десятков тестов. Теперь давайте не будем слишком углубляться в «когда вы вносите изменения, только несколько тестов должны сломаться. Вы не должны видеть 30-40 неудачных тестов». Ну, конечно, это здорово с идеальным набором тестов, но иногда у вас просто не будет этого. Или вы будете вносить изменения в промежуточное программное обеспечение, которое влияет на все вызовы, и у вас будет масса сбоев. Когда у вас есть 30-40 тестов, если не только 10, вы обнаружите, что просеиваете множество выходных данных pytest, чтобы просто найти тесты, которые не прошли. Из-за этого давайте просто подключимся к выполнению pytests и запишем все наши неудачные тесты в failures.txt
поэтому мы можем очень четко видеть, какие из наших тестов не удались.
Создать новый virtualenv
и установить pytest
в этом. я предполагаю Python3.6
Это должно быть все, что вам нужно.
Напишите простой тест, который мы принудительно провалим в файле tests.py
def test_failed():
assert False
def test_passed():
assert True
Создать conftest.py
и добавьте следующий код.
import pytest
@pytest.hookimpl()
def pytest_sessionstart(session):
print("hello")
Pytest автоматически подберет наш хук из conftest.py
так же, как это было бы с приспособлениями.
Если мы побежим $ pytest tests.py
мы можем увидеть результат следующим образом. (удалил некоторые =
для краткости)
19:45 $ pytest tests.py
hello
=======================...=================== test session starts
Согласно API-документы мы можем видеть, что это был бы подходящий хук для создания нашего failures.txt
файл, так что давайте сделаем это.
Меняйте крючок следующим образом
from pathlib import Path
from _pytest.main import Session
FAILURES_FILE = Path() / "failures.txt"
@pytest.hookimpl()
def pytest_sessionstart(session: Session):
if FAILURES_FILE.exists():
FAILURES_FILE.unlink()
FAILURES_FILE.touch()
мне нравится использовать pathlib
при работе с чем-либо на пути только потому, что это намного приятнее, чем os.path
. Изменение логики довольно простое. Если файл уже существует, удалите его, а затем создайте новый.
Далее нам нужно написать хук, чтобы добавить ошибки теста к нашему failures.txt
файл. Согласно API-документы pytest_runtest_makereport
будет нашим желаемым крючком. Этот хук запускается сразу после запуска тестового примера и получает сам тестовый пример (Item
), а также результат тестового примера (CallInfo
). Добавьте следующее в conftest.py
from _pytest.nodes import Item
from _pytest.runner import CallInfo
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item: Item, call: CallInfo):
outcome = yield
result = outcome.get_result()
if result.when == "call" and result.failed:
try:
with open(str(FAILURE_FILE), "a") as f:
f.write(result.nodeid + "\n")
except Exception as e:
print("ERROR", e)
pass
Поскольку нам не нужно модифицировать сам отчет о тестировании, мы напишем наш как обернутый хук через hookwrapper=True
. API-документы дайте отличный пример упорядочения вызовов ловушек, который является более подробным, чем мои комментарии.
В этот момент, если мы запустим $ pytest tests.py
мы можем видеть, что наш единственный неудачный тест записывается в failures.txt
$ cat failures.txt
tests.py::test_failed
Разработчики в вашей команде могут даже не заметить этот файл, поэтому давайте дадим им небольшое указание. Давайте добавим хук для печати направлений в конце запуска теста.
Снова просматривая API-документы кажется pytest_terminal_summary
наш желаемый крючок.
from _pytest.runner import CallInfo
from _pytest.terminal import TerminalReporter
@pytest.hookimpl(hookwrapper=True)
def pytest_terminal_summary(
terminalreporter: TerminalReporter, exitstatus: int, config: Config
):
yield
print(f"Failures outputted to: {FAILURE_FILE}")
print(f"to see run\ncat {FAILURE_FILE}")
В нашем случае, я думаю, имеет смысл запускать все остальные хуки до запуска нашего, чтобы мы могли гарантировать, что наши направления будут последними, что напечатано на терминале. Мы снова будем использовать hookwrapper=True
и поместите наш код ПОСЛЕ yield
утверждение. В нашем случае мы ничего не делаем с результатом yield
поэтому мы можем просто вызвать его и ничему не присвоить. С этим изменением кода запустите $ pytest tests.py
снова и мы можем видеть следующее.
tests.py:3: AssertionError
Failures outputted to: failures.txt
to see run
cat failures.txt
=====================...== 1 failed, 1 passed in 0.04s=====...===
Я надеюсь, что это послужило хорошим примером использования написания пользовательских хуков в реальном мире. pytest
. Не стесняйтесь обращаться ко мне с любыми вопросами или отзывами относительно этого поста.