Python · Тестирование с pytest · Продвинутый
Фабрики фикстур и тестовые данные
Создавайте гибкие тестовые данные с помощью фабричных фикстур и библиотеки Faker.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Фабричные фикстуры на практике: пользователи, продукты и Faker
#Справочная карточка: фабричные фикстуры и Faker
#Упражнения:
Написать фабричную фикстуру make_user
## test_factory_basic.py
import pytest
@pytest.fixture
def make_user():
def _factory(username='testuser', email=None, role='user', age=25):
# если email равен None, вывести его из username
...
return ...
return _factory
def test_default_user(make_user):
user = make_user()
assert user['role'] == 'user'
assert ... # email содержит username
def test_two_users(make_user):
a = make_user(username='alice')
b = make_user(username='bob')
assert ...
Решение
# test_factory_basic.py
import pytest
@pytest.fixture
def make_user():
def _factory(username='testuser', email=None, role='user', age=25):
if email is None:
email = f'{username}@example.com'
return {'username': username, 'email': email, 'role': role, 'age': age}
return _factory
def test_default_user(make_user):
user = make_user()
assert user['role'] == 'user'
assert user['username'] in user['email']
def test_two_users(make_user):
a = make_user(username='alice')
b = make_user(username='bob')
assert a['username'] != b['username']
Добавить Faker для реалистичных генерируемых данных
## test_faker_factory.py
import pytest
from faker import Faker
@pytest.fixture(scope='session', autouse=True)
def seed_faker():
Faker.seed(0)
@pytest.fixture
def make_user():
fake = Faker()
def _factory(**kwargs):
defaults = {
'username': ...,
'email': ...,
'first_name': ...,
'last_name': ...,
}
defaults.update(kwargs)
return defaults
return _factory
def test_users_have_unique_emails(make_user):
users = [make_user() for _ in range(5)]
emails = [u['email'] for u in users]
assert len(set(emails)) == len(emails)
Решение
# test_faker_factory.py
import pytest
from faker import Faker
@pytest.fixture(scope='session', autouse=True)
def seed_faker():
Faker.seed(0)
@pytest.fixture
def make_user():
fake = Faker()
def _factory(**kwargs):
defaults = {
'username': fake.user_name(),
'email': fake.email(),
'first_name': fake.first_name(),
'last_name': fake.last_name(),
}
defaults.update(kwargs)
return defaults
return _factory
def test_users_have_unique_emails(make_user):
users = [make_user() for _ in range(5)]
emails = [u['email'] for u in users]
assert len(set(emails)) == len(emails)
Написать фабрику make_product и создать несколько продуктов
## test_products_factory.py
import pytest
@pytest.fixture
def make_product():
def _factory(name='Test Product', slug=None, price=9.99, in_stock=True):
if slug is None:
slug = name.lower().replace(' ', '-')
return {'name': name, 'slug': slug, 'price': price, 'in_stock': in_stock}
return _factory
def test_three_products(make_product):
products = [
make_product(price=...),
make_product(price=...),
make_product(price=...),
]
assert all(...)
assert sum(p['price'] for p in products) == ...
Решение
# test_products_factory.py
import pytest
@pytest.fixture
def make_product():
def _factory(name='Test Product', slug=None, price=9.99, in_stock=True):
if slug is None:
slug = name.lower().replace(' ', '-')
return {'name': name, 'slug': slug, 'price': price, 'in_stock': in_stock}
return _factory
def test_three_products(make_product):
products = [
make_product(price=5.00),
make_product(price=10.00),
make_product(price=20.00),
]
assert all(p['in_stock'] for p in products)
assert sum(p['price'] for p in products) == pytest.approx(35.00)
Написать фабрику для вложенной структуры
## test_order_factory.py
import pytest
@pytest.fixture
def make_user():
def _factory(**kwargs):
defaults = {'username': 'testuser', 'email': '[email protected]', 'role': 'user'}
defaults.update(kwargs)
return defaults
return _factory
@pytest.fixture
def make_product():
def _factory(**kwargs):
defaults = {'name': 'Widget', 'price': 9.99, 'in_stock': True}
defaults.update(kwargs)
return defaults
return _factory
@pytest.fixture
def make_order(make_user, make_product):
def _factory(**kwargs):
defaults = {
'user': ...,
'items': [...],
'status': 'pending',
}
defaults.update(kwargs)
return defaults
return _factory
def test_order_default_has_one_item(make_order):
order = make_order()
assert len(order['items']) == 1
def test_order_custom_items(make_order, make_product):
p1 = make_product(name='A')
p2 = make_product(name='B')
order = make_order(items=[p1, p2])
assert len(order['items']) == 2
Решение
# test_order_factory.py
import pytest
@pytest.fixture
def make_user():
def _factory(**kwargs):
defaults = {'username': 'testuser', 'email': '[email protected]', 'role': 'user'}
defaults.update(kwargs)
return defaults
return _factory
@pytest.fixture
def make_product():
def _factory(**kwargs):
defaults = {'name': 'Widget', 'price': 9.99, 'in_stock': True}
defaults.update(kwargs)
return defaults
return _factory
@pytest.fixture
def make_order(make_user, make_product):
def _factory(**kwargs):
defaults = {
'user': make_user(),
'items': [make_product()],
'status': 'pending',
}
defaults.update(kwargs)
return defaults
return _factory
def test_order_default_has_one_item(make_order):
order = make_order()
assert len(order['items']) == 1
def test_order_custom_items(make_order, make_product):
p1 = make_product(name='A')
p2 = make_product(name='B')
order = make_order(items=[p1, p2])
assert len(order['items']) == 2
Скомбинировать фабричную фикстуру с parametrize
## test_access.py
import pytest
def has_access(user, resource):
...
@pytest.fixture
def make_user():
def _factory(**kwargs):
defaults = {'username': 'testuser', 'role': 'user'}
defaults.update(kwargs)
return defaults
return _factory
@pytest.mark.parametrize('role,resource,expected', [
('admin', 'secret', True),
('user', 'public', True),
('user', 'secret', False),
('guest', 'private', False),
])
def test_access(make_user, role, resource, expected):
user = make_user(role=role)
assert has_access(user, resource) is expected
Решение
# test_access.py
import pytest
def has_access(user, resource):
return user['role'] == 'admin' or resource == 'public'
@pytest.fixture
def make_user():
def _factory(**kwargs):
defaults = {'username': 'testuser', 'role': 'user'}
defaults.update(kwargs)
return defaults
return _factory
@pytest.mark.parametrize('role,resource,expected', [
('admin', 'secret', True),
('user', 'public', True),
('user', 'secret', False),
('guest', 'private', False),
])
def test_access(make_user, role, resource, expected):
user = make_user(role=role)
assert has_access(user, resource) is expected