Введение
Примеры
Настройка py.test
py.test
является одним из нескольких библиотек сторонних тестирующих , которые доступны для Python. Он может быть установлен с помощью pip
с
pip install pytest
Код для тестирования
Скажем , мы тестируем функцию добавления в projectroot/module/code.py
:
# projectroot/module/code.py
def add(a, b):
return a + b
Код тестирования
Мы создаем тестовый файл в projectroot/tests/test_code.py
.Файл должен начинаться с test_
, чтобы быть признанным в качестве файла тестирования.
# projectroot/tests/test_code.py
from module import code
def test_add():
assert code.add(1, 2) == 3
Запуск теста
Из projectroot
мы просто запустить py.test
:
# ensure we have the modules
$ touch tests/__init__.py
$ touch module/__init__.py
$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items
tests/test_code.py .
================================================ 1 passed in 0.01 seconds ================================================
Неудачные тесты
Неудачный тест предоставит полезный вывод о том, что пошло не так:
# projectroot/tests/test_code.py
from module import code
def test_add__failing():
assert code.add(10, 11) == 33
Результаты:
$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items
tests/test_code.py F
======================================================== FAILURES ========================================================
___________________________________________________ test_add__failing ____________________________________________________
def test_add__failing():
> assert code.add(10, 11) == 33
E assert 21 == 33
E + where 21 = <function add at 0x105d4d6e0>(10, 11)
E + where <function add at 0x105d4d6e0> = code.add
tests/test_code.py:5: AssertionError
================================================ 1 failed in 0.01 seconds ================================================
Введение в тестовые приспособления
Более сложные тесты иногда требуют настройки перед запуском кода, который вы хотите протестировать. Это можно сделать в самой тестовой функции, но в результате вы получаете большие тестовые функции, которые делают так много, что трудно сказать, где остановка установки и начало теста. Вы также можете получить много повторяющихся кодов установки между различными функциями тестирования.
Наш кодовый файл:
# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
self.foo = 1
self.bar = 2
Наш тестовый файл:
# projectroot/tests/test_stuff.py
import pytest
from module import stuff
def test_foo_updates():
my_stuff = stuff.Stuff()
my_stuff.prep()
assert 1 == my_stuff.foo
my_stuff.foo = 30000
assert my_stuff.foo == 30000
def test_bar_updates():
my_stuff = stuff.Stuff()
my_stuff.prep()
assert 2 == my_stuff.bar
my_stuff.bar = 42
assert 42 == my_stuff.bar
Это довольно простые примеры, но если наш Stuff
объекта нужно намного больше настроек, это будет получить громоздким. Мы видим, что между нашими тестами есть некоторый дублированный код, поэтому давайте сначала соберем его в отдельную функцию.
# projectroot/tests/test_stuff.py
import pytest
from module import stuff
def get_prepped_stuff():
my_stuff = stuff.Stuff()
my_stuff.prep()
return my_stuff
def test_foo_updates():
my_stuff = get_prepped_stuff()
assert 1 == my_stuff.foo
my_stuff.foo = 30000
assert my_stuff.foo == 30000
def test_bar_updates():
my_stuff = get_prepped_stuff()
assert 2 == my_stuff.bar
my_stuff.bar = 42
assert 42 == my_stuff.bar
Это выглядит лучше , но у нас еще есть my_stuff = get_prepped_stuff()
вызов захламление наши тестовые функции.
py.test светильники на помощь!
Светильники являются гораздо более мощными и гибкими версиями функций настройки теста. Они могут сделать намного больше, чем мы используем здесь, но мы сделаем это по одному шагу за раз.
Сначала мы изменим get_prepped_stuff
к арматуре называется prepped_stuff
.Вы хотите называть свои приборы именами существительными, а не глаголами, потому что приборы в конечном итоге будут использоваться в самих тестовых функциях. @pytest.fixture
указывает , что эта конкретная функция должна быть обработана как приспособление , а не регулярной функции.
@pytest.fixture
def prepped_stuff():
my_stuff = stuff.Stuff()
my_stuff.prep()
return my_stuff
Теперь мы должны обновить тестовые функции, чтобы они использовали прибор. Это делается путем добавления параметра к их определению, который точно совпадает с именем прибора. Когда py.test выполняется, он запускает прибор перед запуском теста, а затем передает возвращаемое значение прибора в тестовую функцию через этот параметр. (Обратите внимание , что светильники не нужно возвращать значение, они могут сделать другие настройки вещи вместо того, чтобы , как вызов внешнего ресурса, устраивая вещи в файловой системе, помещая значения в базе данных, независимо от тестов нужно для установки)
def test_foo_updates(prepped_stuff):
my_stuff = prepped_stuff
assert 1 == my_stuff.foo
my_stuff.foo = 30000
assert my_stuff.foo == 30000
def test_bar_updates(prepped_stuff):
my_stuff = prepped_stuff
assert 2 == my_stuff.bar
my_stuff.bar = 42
assert 42 == my_stuff.bar
Теперь вы можете понять, почему мы назвали его существительным. но my_stuff = prepped_stuff
линия довольно много бесполезной, так что давайте просто использовать prepped_stuff
непосредственно вместо этого.
def test_foo_updates(prepped_stuff):
assert 1 == prepped_stuff.foo
prepped_stuff.foo = 30000
assert prepped_stuff.foo == 30000
def test_bar_updates(prepped_stuff):
assert 2 == prepped_stuff.bar
prepped_stuff.bar = 42
assert 42 == prepped_stuff.bar
Теперь мы используем светильники! Мы можем пойти дальше, изменив область действия прибора (чтобы он запускался только один раз для каждого тестового модуля или сеанса выполнения набора тестов, а не один раз для каждой тестовой функции), создавая приборы, использующие другие приборы, параметризовав прибор (так, чтобы прибор и все тесты с использованием этого прибора выполняются несколько раз, по одному разу для каждого параметра, переданного устройству), приборы, которые считывают значения из вызывающего их модуля ... как упоминалось ранее, приборы имеют гораздо большую мощность и гибкость, чем обычная функция настройки.
Очистка после испытаний.
Допустим, наш код вырос и наш объект Stuff теперь нуждается в специальной очистке.
# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
self.foo = 1
self.bar = 2
def finish(self):
self.foo = 0
self.bar = 0
Мы могли бы добавить некоторый код для вызова очистки внизу каждой тестовой функции, но приспособления предоставляют лучший способ сделать это. Если добавить функцию к арматуре и зарегистрировать его в качестве финализации, код в функции финализатора будет вызван после испытания с помощью прибора выполняется. Если область действия прибора больше, чем одна функция (например, модуль или сеанс), финализатор будет выполнен после завершения всех тестов в области, то есть после завершения работы модуля или в конце всего сеанса выполнения теста. ,
@pytest.fixture
def prepped_stuff(request): # we need to pass in the request to use finalizers
my_stuff = stuff.Stuff()
my_stuff.prep()
def fin(): # finalizer function
# do all the cleanup here
my_stuff.finish()
request.addfinalizer(fin) # register fin() as a finalizer
# you can do more setup here if you really want to
return my_stuff
Использование функции финализатора внутри функции может быть довольно сложно понять на первый взгляд, особенно если у вас есть более сложные приборы. Вместо этого вы можете использовать выход приспособление , чтобы сделать то же самое с более читаемом потоком выполнения. Единственное отличие в том , что вместо того , чтобы использовать return
мы используем yield
на части прибора , где установка делаются и контроль должен идти к тестовой функции, затем добавьте весь код очистки после yield
.Мы также украсить его как yield_fixture
так , что py.test знает , как справиться с этим.
@pytest.yield_fixture
def prepped_stuff(): # it doesn't need request now!
# do setup
my_stuff = stuff.Stuff()
my_stuff.prep()
# setup is done, pass control to the test functions
yield my_stuff
# do cleanup
my_stuff.finish()
И это завершает вступление к тестовым приспособлениям!
Для получения дополнительной информации см официальной py.test арматуры документация и документация арматуре официального выхода