Всем привет
В первой части немного познакомились с фреймворком для тестирования py.test, а в этой немного углубимся и посмотрим на дополнительные инструменты py.test.
Что такое test fixtures
В тестировании обычно под этим понимают флоу теста, его контекст.
В py.test предусмотрены следующие типы фикстур: встроенные фикстуры, вспомогательные функции, декораторы
setup/teardown
Часто перед запуском теста нужно провести базовые подготовления. Например, это может быть настройка окружения, запуск тестовых сервисов, подготовка тестовых данных, инициализация соединения с базами и проч. Для выполнения этих операций в py.test входит небольшой набор вспомогательных функций - несколько пар setup/teardown.
setup_module(module)
teardown_module(module)
Это функции которые вызываются перед и после выполнения всех тестов (в прошлой части более подробно об организации тестов)
Данную пару удобно использовать когда подготовка флоу происходит однажды - перед запуском всех тестов.
setup_function(function)
teardown_function(function)
Делают тоже самое что и предыдущие функции, с единственным исключением - функции вызываются перед/после каждым выполнением всех тестовых сценариев.
Рассмотрим небольшой надуманный пример, для понимания механизма
#fixtures.py
def setup_module(module):
print('\nsetup_module()')
def teardown_module(module):
print('\nteardown_module()')
def setup_function(function):
print('\nsetup_function()')
def teardown_function(function):
print('\nteardown_function()')
def test_first():
print('\ntest_first()')
def test_second():
print('\ntest_second()')
class TestMyClass:
@classmethod
def setup_class(cls):
print ('\nsetup_class()')
@classmethod
def teardown_class(cls):
print ('teardown_class()')
def setup_method(self, method):
print ('\nsetup_method()')
def teardown_method(self, method):
print ('\nteardown_method()')
def test_first(self):
print('test_first()')
def test_second(self):
print('test_second()')
>>>py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 4 items
fixtures.py:17: test_first
setup_module()
setup_function()
test_first()
PASSED
teardown_function()
fixtures.py:21: test_second
setup_function()
test_second()
PASSED
teardown_function()
fixtures.py:40: TestMyClass.test_first
setup_class()
setup_method()
test_first()
PASSED
teardown_method()
fixtures.py:43: TestMyClass.test_second
setup_method()
test_second()
PASSED
teardown_method()
teardown_class()
teardown_module()
========================== 4 passed in 0.02 seconds ===========================
Встроенные фикстуры
В py.test предусмотрен обширный набор встроенных фикстур с полным набором можно ознакомиться, выполнив в консоле следующую команду
>>>py.test --fixtures
Для ознакомления в данном посте рассмотри одну из встроенных фикстур tempdir
temdir
Очень полезная вещь, предназначена для работы с темповыми данными. Т.е. в темповой директории можно хранить любые временные файлы. Например, это могут быть логи тестируемых программ или какой-то контекcт теста, который может понадобиться для воспроизведения бага.
Использовать крайне просто
#
fixtures.py
def test_file(tmpdir):
f = tmpdir.mkdir('logs').join('test.log')
print(tmpdir.strpath)
f.write('bla bla')
with open(f.strpath, 'r') as fp:
print(fp.read())
>>>py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 1 items
fixtures.py:161: test_file \temp\pytest-76\test_file0
bla bla
PASSED
========================== 1 passed in 0.03 seconds ===========================
Полезные фикстуры
Для итерации по входным данным можно использовать полезный декоратор py.test.mark.parametrize
#fixtures.py
@py.test.mark.parametrize('num', [i for i in range(2)])
def test(num):
assert num % 2
py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 2 items
fixtures.py:182: test[0] FAILED
fixtures.py:182: test[1] PASSED
================================== FAILURES ===================================
___________________________________ test[0] ___________________________________
num = 0
@py.test.mark.parametrize('num', [i for i in range(2)])
def test(num):
> assert num % 2
E assert (0 % 2)
fixtures.py:184: AssertionError
===================== 1 failed, 1 passed in 0.02 seconds ======================
Кастомизация
О кастомных фикстурах более подробно в одном из следующих постов, поэтому здесь совсем небольшой пример.
Для определения собственных инструментов в py.test предусмотрены
#fixtures.py
@pytest.fixture(scope="function")
def before(request):
print('\nbefore()')
def after():
print("\nafter()")
request.addfinalizer(after)
def test_func(before):
print("\ntest_func()")
>>>py.test -s -v fixtures.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- C:\Python27\python.exe
collected 1 items
fixtures.py:14: test_func
before()
test_func()
PASSED
after()
========================== 1 passed in 0.01 seconds ===========================
Вывод
1. Прежде всего это создано для удобства (как собственно и весь py.test)
2. Сокращает время разработки теста
3. делает тест прозрачнее
4. Уменьшает количество копипаста
Ссылки
http://pytest.org/latest/
http://en.wikipedia.org/wiki/Test_fixture
http://pythontesting.net/