Тестирование методов класса с помощью pytest

В документации на pytest перечислены различные примеры для тестовых случаев. Большинство из них показывают проверку функций. Но Im пропускает пример того, как тестировать классы и методы класса. Допустим, у нас есть следующий класс в модуле cool.py, который нам нравится тестировать:

class SuperCool(object):

    def action(self, x):
        return x * x

Как должен выглядеть соответствующий тестовый класс в tests/test_cool.py?

class TestSuperCool():

    def test_action(self, x):
        pass

Как test_action() можно использовать для тестирования action()?

Ответ 1

Все, что вам нужно сделать, чтобы протестировать метод класса, представляет собой экземпляр этого класса и вызывает метод в этом экземпляре:

def test_action(self):
    sc = SuperCool()
    assert sc.action(1) == 1

Ответ 2

Ну, один из способов - просто создать свой объект в методе test и взаимодействовать с ним оттуда:

def test_action(self, x):
    o = SuperCool()
    assert o.action(2) == 4

Вы, очевидно, можете использовать что-то вроде классического теста setup и teardown, используя следующие методы: http://doc.pytest.org/en/latest/xunit_setup.html

Я не уверен на 100%, как они используются, потому что документация для pytest ужасна.

Изменить: да, очевидно, если вы делаете что-то вроде

class TestSuperCool():
    def setup(self):
        self.sc = SuperCool()

    ... 

    # test using self.sc down here

Ответ 3

Я бы использовал любые приборы только для создания тестовой среды (например, подключения к базе данных) или параметризации данных.

Если ваши данные относительно тривиальны, вы можете определить их внутри тестовой системы:

def test_action_without_fixtures():
    sc = SuperCool()
    sc.element = 'snow'
    sc.melt()
    assert sc.element == 'water'

Пример с параметризацией:

@pytest.mark.parametrize("element, expected", [('snow', 'water'), ('tin', 'solder')])
def test_action_with_parametrization(element, expected):
    sc = SuperCool()
    sc.element = element
    sc.melt()
    assert sc.element == expected

Ответ 4

Если инициализация класса стоит дорого, но вам нужно запустить много параматеризованных тестов для метода класса, вы можете изменить определение метода, чтобы разрешить внешние входные данные. Затем вы можете один раз инициализировать вне цикла тестирования и запустить столько методов, сколько вам нужно для метода. Например:

Вместо этого:

class SuperCool():
    def action(self):
        return self.attribute ** 2

Переписать, чтобы разрешить внешний ввод:

class SuperCool():
    def action(self, x=None):
        if x is None:
            x = self.attribute
        return x ** 2

Теперь ваш тестовый скрипт может выглядеть следующим образом:

sc = SuperCool()
@pytest.mark.parametrize("x, y", [(1, 1), (2, 4)])
def test_action_with_parametrization(x, y):
    assert sc.action(x) == y

Но я не опытный программист, так что, надеюсь, это не анти-паттерн XD