Выполнить код до и после каждого теста в py.test?
Я хочу выполнить дополнительные настройки и проверки до и после каждого теста в моем наборе тестов. Я смотрел на светильники, но не уверен, что они правильные. Мне нужно запускать код установки перед каждым тестом, и мне нужно запускать проверки разборки после каждого теста.
Мой вариант использования - проверка кода, который не очищается правильно: он оставляет временные файлы. В моей настройке я буду проверять файлы и в демонтаже я также проверяю файлы. Если есть дополнительные файлы, я хочу, чтобы тест не прошел.
9 ответов
Приспособления py.test являются технически адекватным методом для достижения вашей цели.
Вам просто нужно определить прибор следующим образом:
@pytest.fixture(autouse=True)
def run_around_tests():
# Code that will run before your test, for example:
files_before = # ... do something to check the existing files
# A test function will be run at this point
yield
# Code that will run after your test, for example:
files_after = # ... do something to check the existing files
assert files_before == files_after
Объявив свой прибор с autouse=True
, он будет автоматически вызываться для каждой тестовой функции, определенной в том же модуле.
Тем не менее, есть одна оговорка. Утверждая при установке / демонтаже является спорной практикой. У меня сложилось впечатление, что основным авторам py.test это не нравится (мне это тоже не нравится, так что это может повлиять на мое восприятие), так что по мере продвижения вперед вы можете столкнуться с некоторыми проблемами или грубыми углами.
Вы можете использовать fixture
чтобы достичь желаемого.
import pytest
@pytest.fixture(autouse=True)
def run_before_and_after_tests(tmpdir):
"""Fixture to execute asserts before and after a test is run"""
# Setup: fill with any logic you want
yield # this is where the testing happens
# Teardown : fill with any logic you want
Детальное объяснение
@pytest.fixture(autouse=True)
, из документации: "Иногда вы можете захотеть, чтобы фикстуры вызывались автоматически без явного объявления аргумента функции или декоратора usefixtures". Следовательно, это приспособление будет запускаться каждый раз при выполнении теста.# Setup: fill with any logic you want
, эта логика будет выполняться перед фактическим запуском каждого теста. В вашем случае вы можете добавить свои утверждения assert, которые будут выполняться перед фактическим тестом.yield
, как указано в комментарии, именно здесь происходит тестирование# Teardown : fill with any logic you want
, эта логика будет выполняться после каждого теста. Эта логика гарантированно работает независимо от того, что происходит во время тестов.
Примечание: вpytest
есть разница между провалом теста и ошибкой при выполнении теста. Ошибка означает, что тест каким-то образом не прошел. Ошибка указывает на то, что вы не смогли приступить к выполнению надлежащего теста.
Рассмотрим следующие примеры:
Ошибка утверждения до запуска теста -> ОШИБКА
import pytest
@pytest.fixture(autouse=True)
def run_around_tests():
assert False # This will generate an error when running tests
yield
assert True
def test():
assert True
Assert терпит неудачу после запуска теста -> ERROR
import pytest
@pytest.fixture(autouse=True)
def run_around_tests():
assert True
yield
assert False
def test():
assert True
Сбой теста -> СБОЙ
import pytest
@pytest.fixture(autouse=True)
def run_around_tests():
assert True
yield
assert True
def test():
assert Fail
Тест пройден -> ПРОШЛО
import pytest
@pytest.fixture(autouse=True)
def run_around_tests():
assert True
yield
assert True
def test():
assert True
Светильники именно то, что вы хотите. Вот для чего они предназначены.
Используете ли вы светильники в стиле pytest или настройку и разбор (на уровне модуля, класса или метода) светильников в стиле xUnit, зависит от обстоятельств и личного вкуса.
Судя по тому, что вы описываете, кажется, что вы можете использовать Pytest Autouse.
Или уровень функции стиля xUnit setup_function () / teardown_function ().
Pytest полностью вас охватывает. Настолько, что, возможно, это пожарный шланг информации.
Вы можете использовать настройки уровня модуля / разборки из Pytest.
Вот ссылка
http://pytest.org/latest/xunit_setup.html
Работает следующим образом:
def setup_module(module):
""" setup any state specific to the execution of the given module."""
def teardown_module(module):
""" teardown any state that was previously setup with a setup_module
method."""
Test_Class():
def test_01():
#test 1 Code
Будет звонить setup_module
до этого теста и teardown_module
после завершения теста
Вы можете включить этот прибор в каждый тест-скрипт, чтобы запустить его для каждого теста.
ЕСЛИ вы хотите использовать что-то общее для всех тестов в каталоге
http://pythontesting.net/framework/nose/nose-fixture-reference/
В __init__.py
файл пакета, который вы можете включить после
def setup_package():
'''Set up your environment for test package'''
def teardown_package():
'''revert the state '''
Вы можете использовать декораторы, но программно, поэтому вам не нужно помещать декоратор в каждый метод.
Я предполагаю несколько вещей в следующем коде:
Все методы тестирования называются так: "testXXX()" Декоратор добавляется в тот же модуль, в котором реализованы методы тестирования.
def test1():
print ("Testing hello world")
def test2():
print ("Testing hello world 2")
#This is the decorator
class TestChecker(object):
def __init__(self, testfn, *args, **kwargs):
self.testfn = testfn
def pretest(self):
print ('precheck %s' % str(self.testfn))
def posttest(self):
print ('postcheck %s' % str(self.testfn))
def __call__(self):
self.pretest()
self.testfn()
self.posttest()
for fn in dir() :
if fn.startswith('test'):
locals()[fn] = TestChecker(locals()[fn])
Теперь, если вы вызываете методы испытаний...
test1()
test2()
Вывод должен быть примерно таким:
precheck <function test1 at 0x10078cc20>
Testing hello world
postcheck <function test1 at 0x10078cc20>
precheck <function test2 at 0x10078ccb0>
Testing hello world 2
postcheck <function test2 at 0x10078ccb0>
Если у вас есть методы тестирования в качестве методов класса, подход также действителен. Например:
class TestClass(object):
@classmethod
def my_test(cls):
print ("Testing from class method")
for fn in dir(TestClass) :
if not fn.startswith('__'):
setattr(TestClass, fn, TestChecker(getattr(TestClass, fn)))
Призыв к TestClass.my_test()
напечатает:
precheck <bound method type.my_test of <class '__main__.TestClass'>>
Testing from class method
postcheck <bound method type.my_test of <class '__main__.TestClass'>>
Это старый вопрос, но я лично нашел другой способ из документации : используйте
pytest.ini
файл :
[pytest]
usefixtures = my_setup_and_tear_down
import pytest
@pytest.fixture
def my_setup_and_tear_down():
# SETUP
# Write here the logic that you need for the setUp
yield # this statement will let the tests execute
# TEARDOWN
# Write here the logic that you need after each tests
Об операторе yield и о том, как он позволяет запускать тест: ЗДЕСЬ
Посмотрите на Python декораторы https://wiki.python.org/moin/PythonDecorators
Была аналогичная проблема, но затем для перезагрузки URL-адресов в модульных тестах, основанных на настройке. Поскольку это был один из первых хитов, я оставляю это здесь для других разработчиков. Эта тема вдохновила меня на создание этого многоразового решения. Это приспособление pytest , которое устанавливает настройку перед началом модульного теста, затем перезагружает корневой URL-адрес и client.urls (добавьте больше, если у вас есть больше приложений с динамическими URL-адресами в зависимости от настройки), выход (выполните модульный тест) и все, что происходит после выхода, является разборкой. При разборе вернул все как было раньше. Значение; отключите флаг функции и снова перезагрузите маршруты. Таким образом, маршруты не попадают в другие модульные тесты.
Я использовал Django3.2, pytest 7.2 и Python 3.10.
# contents of conftest.py (fixtures)
import sys
from importlib import import_module, reload
import pytest
from django.urls import clear_url_caches
def _reload_urls(root_urlconf: str):
# first clear url cache
clear_url_caches()
# then reload the root url-config
if root_urlconf in sys.modules:
reload(import_module(root_urlconf))
# then reload all the app specific urls (for now just one is enough)
if "client.urls" in sys.modules:
reload(import_module("client.urls"))
@pytest.fixture()
def use_legacy_donation_module(settings):
# setup
settings.PROJECT_USE_NEW_DONATION_MODULE = False
_reload_urls(settings.ROOT_URLCONF)
# unit test
yield
# teardown
settings.PROJECT_USE_NEW_DONATION_MODULE = True
_reload_urls(settings.ROOT_URLCONF)
Пример модульного теста, который загружает разные маршруты в зависимости от настроек. PROJECT_USE_NEW_DONATION_MODULE
# contents unit_test.py
def test_renders_form(client, use_legacy_donation_module):
response = client.get(reverse("donations:legacy_embed"))
assert response.template_name == ["legacy_embed/form.html"]
Светильники по умолчанию имеют scope=function
, Итак, если вы просто используете определение, такое как
@pytest.fixture
def fixture_func(self)
По умолчанию используется значение (scope='function').
Таким образом, любые финализации в функции прибора будут вызываться после каждого теста.
Ссылка: 1. http://programeveryday.com/post/pytest-creating-and-using-fixtures-for-streamlined-testing/