Как принять аргументы из консоли при запуске теста pytest

Всем добрый день, вопрос состоит в следующем
У меня есть 3 окружения: dev stage prod
Соответственно для каждого из 3 окружений есть свой конфиг (Креды к базе данных, логины пароли ссылки и тд.)
Все это дело я собираю с помощью Jenkins+Pytest
Как мне передать в коммандной строке параметр для каждого из окружений?
Мне нужно что бы я мог запустить следующее

pytest test_login.py --env 'prod'
pytest test_login.py --env 'dev'
pytest test_login.py --env 'stage'

Соответствнно мне нужно как то принять аргумент и использовать файл с конфигом нужный.
Как использовать файл скрипт я напишу сам нет ничего сложного, а вот как мне принять этот аргумент есть вопрос

Резюмирую вопрос:
Нужна функция которая примет из строки типа:
pytest test_login.py --env ‘stage’
аргумент env=stage
и вернет его для дальнейшей обработки

Есть предположение что это что то должно быть в в conftest.py

Вопрос второй, может кто то знает как переопределить место хранения файла conftest.py не хочется его хранить в корне проекта.

Нужно добавить pytest_addoption в conftest, а потом брать значение переменной в фикстуре из request:

def pytest_addoption(parser):
    parser.addoption(
        '--env', action='store', default='dev', help='available environments: dev, stage, prod'
    )

@pytest.fixture
def driver(request):
    env = request.config.getoption('--env')

Официальная дока: https://docs.pytest.org/en/latest/example/simple.html

conftest файл можно хранить в папке с тестами и этих файлов может быть несколько, но это фича связана с разграничением областей видимости. К примеру, есть 2 папки с тестами: foo и bar. Для тестов из папки foo будут применяться фикстуры из корневого conftest файла и из того conftest файла, что в папке foo, conftest из папки bar будет игнорироваться

Первые грабли на которые вы наступите при таком вынесении ключей - сменили базу на деве. Или не дай бог положили все в кубер без прямого доступа к базе стейджа. Вообщем случаев может быть очень много и вы будете создавать на каждый случай конфиг? У вас конфиги тогда будут отличаться одной строчкой. Вот вам совет пока вы не запилили много тестов на вашей реализации - делайте ключ в pytest_addoption на каждую сущность типа базы, юзеров, методов апи, локаторов - все отдельно. Да, разрастется строка запуска, но вам никто не мешает ее записать в sh скрипт. Но зато вы получите шикарную возможность изменить значение одного-двух ключей и тесты побегут уже в другом направлении.

2 лайка

При такое реализации мне придется засовывать фикстуру в каждый тест, что не очень удобно.

Здесь есть несколько путей, чтобы не прописывать фикстуру каждому тесту вручную.

  1. Можно выставить фикстуре флаг autouse:
@pytest.fixture(autouse=True)
def configure(request):
    env = request.config.getoption('--env')
    ...

В этом случае фикстура запуститься сама для каждого теста

  1. Можно объедить тесты в тестовый класс, а классу прописать декоратор:
@pytest.mark.usefixtures("configure")
class MyTests:
   ...

Тогда фикстура будет применяться к каждому тесту в классе

Можно также поменять scope для фикстуры, чтобы она запускалась 1 раз для всех тестов, а не для каждого по отдельности:

@pytest.fixture(scope='session')
1 лайк

Вам не нужно мудрить с этим. Просто создайте фикстуру, которая получает значение аргумента из pytest_addoption и прокидывайте в тест уже распарсеный конфиг из файла. Прокидывание в тест конфига это не зашкварно.

1 лайк

немного из conftest.py

def pytest_addoption(parser):
    """Declaring the command-line options for test run"""
    parser.addoption('--host',
                     default='staging',
                     help='host options: "staging", "production", or your own host for local testing')
    parser.addoption('--headless',
                     default='true',
                     help='headless options: "true" or "false"')
    parser.addoption('--browser',
                     default='chrome',
                     help='option to define type of browser')
    parser.addoption('--browser_type',
                     default='mobile',
                     help='option to define web or mobile browser')


@fixture(autouse=True)
def driver_type(request):
    """Return type of browser mobile or web
    :param request:
    :return:
    """
    browser_type = request.config.getoption('--browser_type')
    yield browser_type


@fixture(autouse=True)
def email_password():
    """
    Return email password
    """
    password = os.environ.get('EMAIl_PASSWORD')
    yield password



@fixture(autouse=True)
def host(request):
    """Return the target host
    :param request:
    :return:
    """
    # get host value
    cli_value = request.config.getoption('--host')

    if cli_value == '' or cli_value == 'staging':
        domain = HOST_PARAM_STAGING
    elif cli_value == 'prod':
        domain = HOST_PARAM_PROD
    else:
        domain = cli_value
    yield domain

1 лайк

Кстати эту часть можно переписать с action="store_true", default=True, и вы сразу будете получать bool при парсинге опшенов

parser.addoption('--headless',
                 default='true',
                 help='headless options: "true" or "false"')
1 лайк