Python · Тестирование с pytest · Начальный
Фикстуры
Используйте @pytest.fixture для переиспользуемой настройки без лишнего кода.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Паттерны фикстур, которые используются каждый день
#Краткий справочник по фикстурам
#Упражнения:
Фикстура, используемая двумя тестами
#Создайте `@pytest.fixture` с именем `numbers`, которая возвращает `[3, 1, 4, 1, 5, 9, 2, 6]`. Напишите две тест-функции, принимающие её как параметр: одна проверяет `sum(numbers) == 31`, другая -- `max(numbers) == 9`.
import pytest
@pytest.fixture
def numbers():
pass # вернуть список
def test_sum(numbers):
pass
def test_max(numbers):
pass
Решение
import pytest
@pytest.fixture
def numbers():
return [3, 1, 4, 1, 5, 9, 2, 6]
def test_sum(numbers):
assert sum(numbers) == 31
def test_max(numbers):
assert max(numbers) == 9
Фикстура с yield для очистки
#Напишите `@pytest.fixture` с именем `temp_csv`, которая создаёт временный CSV-файл с двумя строками данных через `tmp_path`, возвращает путь к файлу через yield, затем удаляет файл. Напишите тесты, читающие файл и проверяющие количество строк и заголовок.
import pytest
@pytest.fixture
def temp_csv(tmp_path):
path = tmp_path / 'data.csv'
# записать две строки в path
yield path
# очистка
def test_csv_has_two_lines(temp_csv):
pass
Решение
import pytest
@pytest.fixture
def temp_csv(tmp_path):
path = tmp_path / 'data.csv'
path.write_text('name,age\nAlice,30\n')
yield path
if path.exists():
path.unlink()
def test_csv_has_two_lines(temp_csv):
lines = temp_csv.read_text().strip().splitlines()
assert len(lines) == 2
def test_csv_header(temp_csv):
first_line = temp_csv.read_text().splitlines()[0]
assert first_line == 'name,age'
Фикстура конфигурации для тестируемой функции
#Напишите `@pytest.fixture` с именем `app_config`, возвращающую `{'base_url': 'https://api.example.com', 'timeout': 10, 'retries': 3}`. Напишите `build_url(config, path)`, которая соединяет `base_url` и `path` без двойных слешей. Протестируйте её с помощью фикстуры.
import pytest
@pytest.fixture
def app_config():
pass # вернуть словарь конфигурации
def build_url(config, path):
pass # соединить base_url и path
def test_build_url(app_config):
pass
Решение
import pytest
@pytest.fixture
def app_config():
return {'base_url': 'https://api.example.com', 'timeout': 10, 'retries': 3}
def build_url(config, path):
return config['base_url'].rstrip('/') + '/' + path.lstrip('/')
def test_build_url(app_config):
url = build_url(app_config, '/users')
assert url == 'https://api.example.com/users'
def test_build_url_no_double_slash(app_config):
url = build_url(app_config, 'products')
assert '//' not in url.split('https://')[-1]
def test_config_timeout(app_config):
assert app_config['timeout'] == 10
Использование двух фикстур в одном тесте
#Создайте две фикстуры: `base_url`, возвращающую `'https://api.example.com'`, и `headers`, возвращающую `{'Content-Type': 'application/json', 'Accept': 'application/json'}`. Напишите тест, использующий обе, и проверяющий, что URL использует HTTPS, а заголовки содержат нужные ключи.
import pytest
@pytest.fixture
def base_url():
pass
@pytest.fixture
def headers():
pass
def test_request_setup(base_url, headers):
pass
Решение
import pytest
@pytest.fixture
def base_url():
return 'https://api.example.com'
@pytest.fixture
def headers():
return {'Content-Type': 'application/json', 'Accept': 'application/json'}
def test_request_setup(base_url, headers):
assert base_url.startswith('https://')
assert 'Content-Type' in headers
assert headers['Content-Type'] == 'application/json'
assert 'Accept' in headers
Цепочка фикстур
#Создайте три фикстуры цепочкой: `db_config` возвращает словарь с `host` и `port`, `db_connection` принимает `db_config` и возвращает `{'status': 'connected', ...}`, `db_cursor` принимает `db_connection` и возвращает `{'query': None, 'connection': ...}`. Напишите тест, который запрашивает только `db_cursor`.
import pytest
@pytest.fixture
def db_config():
pass
@pytest.fixture
def db_connection(db_config):
pass
@pytest.fixture
def db_cursor(db_connection):
pass
def test_cursor_ready(db_cursor):
pass
Решение
import pytest
@pytest.fixture
def db_config():
return {'host': 'localhost', 'port': 5432}
@pytest.fixture
def db_connection(db_config):
return {'status': 'connected', 'host': db_config['host'], 'port': db_config['port']}
@pytest.fixture
def db_cursor(db_connection):
return {'query': None, 'connection': db_connection}
def test_cursor_ready(db_cursor):
assert db_cursor['query'] is None
assert db_cursor['connection']['status'] == 'connected'
assert db_cursor['connection']['host'] == 'localhost'