Как мне обработать несколько утверждений в одном тесте Python? - PullRequest
13 голосов
/ 22 марта 2012

Эта проблема возникла при выполнении одного теста с несколькими независимыми режимами отказа из-за наличия нескольких выходных потоков. Я также хотел показать результаты утверждения данных во всех этих режимах, независимо от того, какой из них вышел из строя первым. У юнит-теста Python нет такой возможности, кроме использования Suite для представления одного теста, что было неприемлемо, поскольку мой единственный тест всегда нужно было выполнять как один модуль; это просто не отражает природу вещи.

Практическим примером является тестирование объекта, который также генерирует журнал. Вы хотите подтвердить вывод его методов, но вы также хотите подтвердить вывод журнала. Для двух выходных данных требуются разные тесты, которые могут быть аккуратно выражены в виде двух стандартных выражений утверждений, но вы также не хотите, чтобы отказ одного из них скрывал возможный отказ другого в тесте. Так что вам действительно нужно проверить оба одновременно.

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

def logFailures(fnList):
    failurelog = []
    for fn in fnList:
        try:
            fn()
        except AssertionError as e:
            failurelog.append("\nFailure %d: %s" % (len(failurelog)+1,str(e)))

    if len(failurelog) != 0:
        raise AssertionError(
            "%d failures within test.\n %s" % (len(failurelog),"\n".join(failurelog))
        )

Что используется так:

def test__myTest():
    # do some work here
    logFailures([
        lambda: assert_(False,"This test failed."),
        lambda: assert_(False,"This test also failed."),
    ])

В результате logFailures () сгенерирует исключение, содержащее журнал всех подтверждений, которые были вызваны в методах из списка.

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

Ответы [ 3 ]

14 голосов
/ 01 января 2013

Я не согласен с доминирующим мнением о том, что для каждого утверждения следует написать метод проверки. Есть ситуации, когда вы хотите проверить несколько вещей в одном методе тестирования. Вот мой ответ, как это сделать:

# Works with unittest in Python 2.7
class ExpectingTestCase(unittest.TestCase):
    def run(self, result=None):
        self._result = result
        self._num_expectations = 0
        super(ExpectingTestCase, self).run(result)

    def _fail(self, failure):
        try:
            raise failure
        except failure.__class__:
            self._result.addFailure(self, sys.exc_info())

    def expect_true(self, a, msg):
        if not a:
            self._fail(self.failureException(msg))
        self._num_expectations += 1

    def expect_equal(self, a, b, msg=''):
        if a != b:
            msg = '({}) Expected {} to equal {}. '.format(self._num_expectations, a, b) + msg
            self._fail(self.failureException(msg))
        self._num_expectations += 1

А вот некоторые ситуации, когда я думаю, что это полезно и не рискованно:

1) Если вы хотите проверить код для разных наборов данных. Здесь у нас есть функция add (), и я хочу протестировать ее с несколькими примерами входных данных. Написать 3 метода испытаний для 3 наборов данных означает повторять себя, что плохо. Особенно, если звонок был более сложным.

class MyTest(ExpectingTestCase):
    def test_multiple_inputs(self):
        for a, b, expect in ([1,1,2], [0,0,0], [2,2,4]):
            self.expect_equal(expect, add(a,b), 'inputs: {} {}'.format(a,b))

2) Если вы хотите проверить несколько выходов функции. Я хочу проверить каждый вывод, но не хочу, чтобы первый сбой маскировал два других.

class MyTest(ExpectingTestCase):
    def test_things_with_no_side_effects(self):
        a, b, c = myfunc()
        self.expect_equal('first value', a)
        self.expect_equal('second value', b)
        self.expect_equal('third value', c)

3) Тестирование с большими затратами на установку. Тесты должны выполняться быстро, иначе люди перестанут их использовать. Некоторые тесты требуют подключения к БД или сети, которое занимает секунду, что действительно замедляет ваш тест. Если вы тестируете само соединение с БД, то вам, вероятно, нужно принять удар по скорости. Но если вы тестируете что-то не связанное, мы хотим выполнить медленную настройку один раз для всего набора проверок.

12 голосов
/ 25 января 2017

При использовании подтеста выполнение не остановится после первого сбоя https://docs.python.org/3/library/unittest.html#subtests

Вот пример с двумя ошибочными утверждениями:

class TestMultipleAsserts(unittest.TestCase):

    def test_multipleasserts(self):
        with self.subTest():
            self.assertEqual(1, 0)
        with self.subTest():
            self.assertEqual(2, 0)

Вывод будет:

======================================================================
FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test.py", line 9, in test_multipleasserts
    self.assertEqual(1, 0)
AssertionError: 1 != 0

======================================================================
FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test.py", line 11, in test_multipleasserts
    self.assertEqual(2, 0)
AssertionError: 2 != 0

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=2)

Вы можете легко завернуть подтест следующим образом

class MyTestCase(unittest.TestCase):
    def expectEqual(self, first, second, msg=None):
        with self.subTest():
            self.assertEqual(first, second, msg)

class TestMA(MyTestCase):
    def test_ma(self):
        self.expectEqual(3, 0)
        self.expectEqual(4, 0)
12 голосов
/ 01 января 2013

Для меня это похоже на чрезмерную инженерность Или:

  • Используйте два подтверждения в одном тестовом примере. Если первое утверждение не удалось, это правда, вы не будете знать, прошло второе утверждение или нет. Но вы все равно исправите код, поэтому исправьте его, и тогда вы узнаете, прошло ли второе утверждение.

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

...