Как мне правильно настроить и разобрать мой класс pytest с помощью тестов?

104

Я использую селен для сквозного тестирования, и я не могу понять, как его использовать setup_classи teardown_classметоды.

Мне нужно настроить браузер в setup_classметоде, затем выполнить кучу тестов, определенных как методы класса, и, наконец, выйти из браузера в teardown_classметоде.

Но логически это кажется плохим решением, потому что на самом деле мои тесты будут работать не с классом, а с объектом. Я selfпередаю параметр внутри каждого тестового метода, чтобы получить доступ к варам объектов:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

И даже создавать экземпляр браузера для класса вроде бы некорректно ... Его надо создавать для каждого объекта отдельно, правда?

Таким образом, мне нужно использовать __init__и __del__методы вместо setup_classи teardown_class?

авасин
источник

Ответы:

94

Согласно коду завершения / выполнения демонтажа Fixture , в настоящее время рекомендуется использовать yieldвместо return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Запуск его приводит к

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

Другой способ написать код разрыва - принять requestобъект -context в вашу функцию фиксации и вызвать его request.addfinalizerметод с функцией, которая выполняет разборку один или несколько раз:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))
Эверетт Тэйвз
источник
Итак, вы копируете это в каждый тестовый файл, в котором вам понадобится ресурс?
Энди Хайден,
@AndyHayden В зависимости от того, как вы пишете свои фикстуры, вы можете поместить его в каждый тестовый файл, где он вам нужен, или вы можете поместить его в файл conftest.py stackoverflow.com/questions/34466027/…
Everett
2
Однако это не настройка класса, верно? Он будет выполняться перед каждым тестовым методом в классе.
malhar
1
В этом конкретном случае он выполняется только при использовании в качестве параметра в методе тестирования. например, resourceparam intest_that_depends_on_resource(self, resource)
Everett
65

Когда вы пишете «тесты, определенные как методы класса» , вы действительно имеете в виду методы класса (методы, которые получают свой класс в качестве первого параметра) или просто обычные методы (методы, которые получают экземпляр в качестве первого параметра)?

Поскольку ваш пример использует selfметоды тестирования, я предполагаю, что последний, поэтому вам просто нужно setup_methodвместо этого использовать :

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

Экземпляр тестового метода передается в setup_methodи teardown_method, но может быть проигнорирован, если вашему коду установки / разборки не требуется знать контекст тестирования. Более подробную информацию можно найти здесь .

Я также рекомендую вам ознакомиться с py.test по светильникам , так как они более мощная концепция.

Бруно Оливейра
источник
1
Фикстуры более слабые, чем методы класса: они не позволяют уничтожать объекты, созданные не ими (что часто действительно необходимо). Кроме того, спасибо за информацию.
wvxvw
Это поразило меня при обновлении кодовой базы с версии pytest 3.0.x до версии 4.x. Некоторый более старый код, используемый setup_classс имитацией методов и т.п., нуждался в модернизации. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos
28

Это может помочь http://docs.pytest.org/en/latest/xunit_setup.html

В моем наборе тестов я группирую свои тестовые примеры по классам. Для настройки и разборки, которые мне нужны для всех тестовых случаев в этом классе, я использую методы setup_class(cls)и teardown_class(cls)class.

И для настройки и разборки, которые мне нужны для каждого тестового примера, я использую setup_method(method)иteardown_method(methods)

Пример:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Теперь, когда я запускаю свои тесты, когда запускается выполнение TestClass, он регистрирует детали того, когда он начинает выполнение, когда он заканчивает выполнение и то же самое для методов.

Вы можете добавить другие шаги по настройке и демонтажу, которые могут быть у вас в соответствующих местах.

Надеюсь, поможет!

Киран Вемури
источник
Привет @Kiran, в чем разница между setup_classvs setup_method?
imsrgadich
1
@imsrgadich Когда вы организуете свои тестовые примеры в классы, <setup / teardown> _class используется для этапов установки и разрыва класса, а <setup / teardown> _method - соответствующие шаги для каждого метода тестового примера.
Киран Вемури
1
Блин ... теперь я понял! застрял на нем на несколько часов. Итак, чтобы взглянуть на вещи в перспективе. Для <setup/teardown>_classвсего класса. Здесь могут быть такие вещи, как установка ссылки на БД или загрузка файла данных. И затем каждый тестовый пример может иметь свою собственную настройку в виде <setup/teardown>_method. Теперь все стало ясно. Большое спасибо!
imsrgadich
24

Как предложил @Bruno, использование приспособлений pytest - еще одно решение, доступное для обоих тестовых классов или даже для простых тестовых функций. Вот пример тестирования функций python2.7 :

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

Итак, бег test_1...дает:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Обратите внимание на то, что stuff_i_setupв фикстуре есть ссылка, позволяющая этому объекту быть setupи torn downдля теста, с которым он взаимодействует. Вы можете представить, что это может быть полезно для постоянного объекта, такого как гипотетическая база данных или какое-то соединение, которое необходимо очищать перед каждым запуском теста, чтобы держать их изолированными.

эко
источник
13

Ваш код должен работать так, как вы ожидаете, если вы добавите @classmethodдекораторы.

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

См. Http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/

Okken
источник
Это в точности то, что написано в документации. Проблема, с которой я столкнулся с документом, заключалась в том, что мне было трудно понять контекст: self традиционно называется self, а не cls, поэтому мне это показалось странным вне контекста самого класса. Киран (см. Выше) предоставляет этот контекст.
Cognitiaclaeves
1
@Cognitiaclaeves "self традиционно называется self, а не cls". Да, selfиспользуется для методов экземпляра, где первый аргумент - это конкретный экземпляр объекта, на котором выполняется операция метода, а clsиспользуется для @classmethods, которые привязаны к класс, а не экземпляр класса (т.е. объект).
code_dredd