Py.test - Сеансовая настройка

1

Я пытаюсь создать хорошую базу вокруг py.test

Некоторые из наших тестов требуют определенных тестовых данных для работы.

Сегодня мы просто указываем mock-объект как аргумент функции и делаем настройку в генераторе, это явно нежелательно.

Вот пример того, как он мог выглядеть сегодня:

def test_something(self, some_data):
    # some_data is unused in the test

Я хотел бы сделать что-то вроде этого:

@uses_some_data
def test_something(self):
    # The data is loaded when the test is run

Хотя я не понял, как это сделать правильно.

Я не могу использовать настройку класса, потому что хочу, чтобы данные были устойчивыми в течение всего сеанса, а не устанавливались/не сбрасывались на каждый тестовый класс.

Моя первая идея заключалась в том, чтобы по-прежнему использовать funcargs, но вместо того, чтобы позволить тестированию иметь funcarg, мы позволяем декоратору запросить funcarg для функции, в основном скрывая уродство.

Проблема в том, что мне нужен объект py.test для запроса funcarg.
Есть ли способ, которым я могу получить такой объект, или это неправильный подход?

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

  • 0
    Не уверен, что правильно понял ваш вопрос, возможно, я что-то упустил. В вашем первом варианте «def test_something (self, some_data): ...» зачем указывать «some_data» в аргументах, если он вам не нужен в теле функции? Смысл funcargs в том, что вы указываете их только в том случае, если они вам действительно нужны - как и в случае с обычными функциями python - вам нужны параметры, только если вы их действительно используете.
  • 0
    Нам требуется установка на основе сеанса, которая загружается только в том случае, если они нужны каким-либо собранным тестам.
Показать ещё 2 комментария
Теги:
py.test
decorator

2 ответа

1
Лучший ответ

Поиграв вокруг, я обнаружил, что это работает:

def pytest_funcarg__some_data(request):
    def create():
        # Load the test data here
        print 'Test data loaded'

    return request.cached_setup(
        setup=create,
        scope='session',
        extrakey='some_data'
    )

def uses_some_data(func):
    # The funcarg is actually requested here
    def wrapper(self, some_data):
        return func
    return wrapper

class TestSomething(object):
    @uses_some_data
    def test_something(self):
        # "Some data" is now available
        pass
1

Вот что-то, что может работать как есть, и, если не будет, надеюсь, укажет вам в правильном направлении.

class TestData(object):
    def __getattr__(self, name):
        if name not in ('data1', 'data2', 'data3'):
            raise AttributeError("TestData has no %s" % name)
        if name == 'data1':
            result = self._generate_data('data1')
            setattr(self.__class__, name, result)
        elif name == 'data2':
            result = self._generate_data('data2')
            setattr(self.__class__, name, result)
        elif name == 'data3':
            result = self._generate_data('data3')
            setattr(self.__class__, name, result)
        return result
    def _generate_data(self, data_name):
        return data_name * int(data_name[-1])

Класс TestData использует метод __getattr__ для генерации данных по мере необходимости и, сохраняя сгенерированную дату обратно в класс (а не экземпляр!), Данные также хранятся для дальнейшего использования.

class uses_some_data(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        global test_data
        test_data = TestData()
        return self.func(*args, **kwargs)

Простой декоратор для установки глобальной привязки имени для test_data. Фактически, эта версия декоратора настолько проста, что ее можно легко заменить связыванием имени уровня на уровне test_data = TestData().

@uses_some_data
def testing_test():
    print(test_data.data2)

И тестовая функция.

Если вам не нравится глобальный уровень test_data вы можете test_data и назначить test_data самой функции:

class uses_some_data(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        self.func.test_data = TestData()
        return self.func(*args, **kwargs)

В этом случае убедитесь, что ваши функции тестирования ссылаются сами

@uses_some_data
def testing_test():
    print(testing_test.test_data.data2)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню