Фикстуры класса: setUpClass

Фикстуры уровня класса: дорогая подготовка один раз на весь класс тестов.

setUpClass / tearDownClass — фикстуры, которые выполняются один раз на весь класс: до первого теста и после последнего.

Когда setUp слишком дорого

setUp вызывается перед каждым тестом. Если подготовка дешёвая (создать список) — отлично. Но если она дорогая — открыть соединение с БД, загрузить большой файл, поднять сервер — повторять её на каждый тест расточительно.

Тогда используют setUpClass: подготовка происходит один раз для всего класса. Это classmethod, он получает cls, а не self, и помечается декоратором @classmethod.

Пример: дорогой ресурс один раз

import unittest

class TestWithResource(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Выполнится ОДИН раз до всех тестов класса
        cls.connection = "соединение открыто"
        print("setUpClass: дорогая подготовка")

    @classmethod
    def tearDownClass(cls):
        # Выполнится ОДИН раз после всех тестов класса
        print("tearDownClass: ресурс освобождён")

    def test_a(self):
        self.assertEqual(self.connection, "соединение открыто")

    def test_b(self):
        self.assertTrue(self.connection)

unittest.main(argv=[''], exit=False, verbosity=2)

Вывод: (видно, что setUpClass и tearDownClass вызвались по разу, хотя тестов два)

test_a (__main__.TestWithResource.test_a) ... ok
test_b (__main__.TestWithResource.test_b) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
setUpClass: дорогая подготовка
tearDownClass: ресурс освобождён

Сообщения «дорогая подготовка» и «ресурс освобождён» появились по одному разу — несмотря на два теста.

Уровни фикстур: сравнение

ФикстураКогдаПараметр
setUp / tearDownвокруг каждого тестаself
setUpClass / tearDownClassраз на классcls (+@classmethod)

Важный нюанс: общее состояние

Данные из setUpClass общие для всех тестов класса. Это плата за скорость: если один тест меняет такой объект, изменение увидят следующие тесты, и появится скрытая зависимость. Поэтому в setUpClass кладите только то, что тесты не меняют (соединения, конфиги, прогретые данные «только для чтения»). Изменяемое состояние оставляйте в setUp.

import unittest

class TestSharedCare(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Только для чтения — общий справочник
        cls.rates = {"USD": 90, "EUR": 100}

    def setUp(self):
        # Изменяемое состояние — у каждого теста своё
        self.basket = []

    def test_rate(self):
        self.assertEqual(self.rates["USD"], 90)

    def test_basket_isolated(self):
        self.basket.append("товар")
        self.assertEqual(len(self.basket), 1)

unittest.main(argv=[''], exit=False, verbosity=2)

Вывод:

test_basket_isolated (__main__.TestSharedCare.test_basket_isolated) ... ok
test_rate (__main__.TestSharedCare.test_rate) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Порядок вызова всех фикстур вместе

Когда в классе есть и фикстуры класса, и обычные, порядок такой: один раз setUpClass в начале, затем для каждого теста пара setUp → тест → tearDown, и в самом конце один раз tearDownClass. То есть фикстуры класса обрамляют весь класс снаружи, а setUp/tearDown — каждый тест внутри. Понимание этого порядка помогает решать, на каком уровне готовить тот или иной ресурс: дорогой и неизменяемый — на уровне класса, дешёвый и свежий для каждого теста — на уровне метода.

Типичная ошибка с setUpClass

Самая частая ловушка — положить в setUpClass объект, который тесты меняют, и получить «летающие» падения, зависящие от порядка тестов. Например, общий список, в который один тест добавляет элемент: следующий тест увидит «лишний» элемент и упадёт. Причём упадёт он не всегда, а в зависимости от того, какой тест выполнился раньше по алфавиту — отлаживать такое мучительно. Правило простое: в setUpClass — только то, что тесты читают, но не изменяют.

Итог

  • setUpClass/tearDownClass запускаются один раз на класс — для дорогой подготовки.
  • Это @classmethod с параметром cls.
  • В них кладут ресурсы «только для чтения»; изменяемое состояние — в setUp.
  • Иначе тесты начнут скрыто влиять друг на друга через общий объект.
Проверьте себя
1. Чем setUpClass отличается от setUp?
AНичем
BsetUpClass вызывается один раз на весь класс, setUp — перед каждым тестом
CsetUpClass быстрее
DsetUp работает только с классами
2. Каким декоратором помечают setUpClass?
A@staticmethod
B@classmethod
C@property
D@patch
3. Что безопасно класть в setUpClass?
AИзменяемое состояние каждого теста
BРесурсы только для чтения (соединения, конфиги), которые тесты не меняют
CСами assert-проверки
DСлучайные числа
Поддержать проект