Як правильно налаштувати та розірвати для мого класу pytest за допомогою тестів?


100

Я використовую селен для наскрізного тестування, і я не можу зрозуміти, як використовувати 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?

Відповіді:


93

Відповідно до доопрацювання приладу / виконання коду відключення , найкраща практика встановлення та розірвання - використовувати 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 Toews

2
Однак це не налаштування класу, так? Він буде виконуватися перед кожним тестовим методом у класі.
Малгар

1
У цьому конкретному випадку він виконується лише тоді, коли використовується як параметр у тестовому методі. напр. resourceпарам уtest_that_depends_on_resource(self, resource)
Еверетт Тьюз

64

Коли ви пишете "тести, визначені як методи класу" , чи справді ви маєте на увазі методи класу (методи, які отримують свій клас як перший параметр), або просто звичайні методи (методи, які отримують екземпляр як перший параметр)?

Оскільки ваш приклад використовує 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)and і teardown_class(cls)classm.

А для налаштування та розірвання, необхідного для кожного з тестових випадків, я використовую 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-fixture/


Це майже саме те, що вказано в документації. Проблема, яка була у мене з документом, полягала в тому, що мені було важко зрозуміти контекст: я традиційно позначається як self, а не cls, тому це здавалося мені дивним, поза контекстом самого класу. Кіран (вище) надає цей контекст.
Cognitiaclaeves

1
@Cognitiaclaeves "self традиційно позначається як self, а не cls" Так, selfвикористовується для методів екземплярів, де першим аргументом є конкретний екземпляр об'єкта, над яким відбувається операція методу, в той час clsяк використовується для @classmethods, які пов'язані з клас, а не екземпляр класу (тобто об’єкт).
code_dredd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.