Разделы портала

Онлайн-тренинги

.
pytest и кастомные аргументы командной строки
12.09.2023 00:00

Автор: Баз Дейкстра (Bas Dijkstra)
Оригинал статьи
Перевод: Ольга Алифанова

Недавно меня спросили по email:

Не могли бы вы, пожалуйста, предложить хороший пример взятия параметра вроде «URL базового окружения» из интерфейса командной строки и передачи его в каждый тест в наборе на основе pytest?

Насколько я знаю, в Java это довольно легко делается. Вот пример командной строки, использующий Maven для запуска тестов и передающий значение http://localhost:8080/api для переменной командной строки env:

mvn clean -Denv=http://localhost:8080/api test

В вашем коде Java можно затем получить доступ к этой переменной:

// 'http://some-default-env.com/api' будет использоваться, как значение по умолчанию, если для env не задано значение
System.getProperty("env", "http://some-default-env.com/api");

Но как сделать это в pytest? И, что еще важнее, как убедиться, что значение, переданное через командную строку, доступно во всех тестах?

Ответ: парсер и фикстуры pytest.

Разберем пошагово.

Регистрация кастомного аргумента командной строки в pytest

Первый шаг, который нужно предпринять для получения возможности передать аргументы командной строки в pytest и их использования в тестах – это регистрация нового аргумента командной строки в pytest. Это можно сделать, добавив вот такой сниппет в файл conftest.py, который хранится в корневой папке проекта:

def pytest_addoption(parser):
parser.addoption(
'--base-url', action='store', default='http://localhost:8080', help='Base URL for the API tests'
)

Это позволяет нам передавать кастомный аргумент –base-url, когда мы вызываем pytest – к примеру, так:

pytest --base-url=http://api.zippopotam.us zip_api_test.py

Если аргумент –base-url при вызове pytest отсутствует, будет использоваться значение по умолчанию – http://localhost:8080.

Parser тут – это объект, доступный pytest по умолчанию. Его можно использовать для парсинга аргументов командной строки, а также для значений файла .ini.

Использование значения аргумента командной строки в наших тестах

Далее нам нужно прочитать значение аргумента командной строки и передать его в тесты. Один из самых удобных способов это сделать – это фикстура pytest:

@pytest.fixture
def base_url(request):
return request.config.getoption('--base-url')

Эта фикстура считывает значение из аргумента командной строки и возвращает его. Теперь ее можно использовать во всех тестах – например, так:

def test_api_endpoint(base_url):
response = requests.get(f'{base_url}/us/90210')
print(response.request.url)
assert response.status_code == 200

Этот тестовый метод использует фикстуру base_url для получения базового URL, переданного через командную строку при вызове pytest (или значение по умолчанию, если оно не было задано в аргументе), для отправки HTTP-запроса к конечной точке с использованием базового URL.

Если теперь прогнать наш тест, используя

pytest -s zip_api_test.py

Он выведет:

http://localhost:8080/us/90210

в качестве конечной точки, использующейся для HTTP-запроса, так как мы не задали никакого значения в аргументе base-url командной строки. Если вызвать pytest так:

pytest -s --base-url=http://api.zippopotam.us zip_api_test.py

Он выведет:

http://api.zippopotam.us/us/90210

в качестве конечной точки, использующейся для запроса, демонстрируя, что значение, переданное в аргументе командной строки, было успешно применено. Победа!

Расширение подхода для нескольких переменных

Но что, если мы хотим передать несколько переменных? Что, если, помимо базового URL, мы, скажем, хотим передать данные авторизации для наших тестов, чтобы не хранить их в коде тестов?

Как я это вижу: возможны два способа решения. В первую очередь в обоих случаях вам нужно зарегистрировать дополнительные аргументы командной строки – например, так:

def pytest_addoption(parser):
parser.addoption(
'--base-url', action='store', default='http://localhost:8080', help='Base URL for the API tests'
)
parser.addoption(
'--username', action='store', default='test_user', help='Username to be used in the API tests'
)
parser.addoption(
'--password', action='store', default='test_password', help='Password to be used in the API tests'
)

Теперь у нас есть два варианта.

Первый: мы храним все значения аргументов командной строки в едином объекте фикстуры – например, так:

@pytest.fixture
def command_line_args(request):
args = {}
args['base_url'] = request.config.getoption('--base-url')
args['username'] = request.config.getoption('--username')
args['password'] = request.config.getoption('--password')
return args

Затем можно использовать эту фикстуру в наших тестах, извлекая значения из словаря в теле тест-метода.

Второй вариант: мы создаем отдельную фикстуру для каждого из аргументов командной строки.

Это сохраняет фикстуры небольшими, но требует использования нескольких фикстур во всех тестах, что может стать несколько громоздким и не очень хорошо масштабируется. Этот подход, однако, добавляет некоторую гибкость при решении, какую фикстуру использовать в каких тестах.

Как и многое другое в жизни, выбор наилучшего подхода зависит от ситуации. В целом я предпочитаю первый вариант. Возможно, есть и третий, о котором я не подумал? Сообщите мне о нем.

Обсудить в форуме