Модульное тестирование для исключений в конструкторе Python

Я просто начинающий программист на Python и вообще задаюсь вопросами о модуле unittest.

У меня есть класс, а в методе __init__ я делаю некоторые утверждения для проверки плохих аргументов. Я хотел бы создать unittest, который проверяет такой AssertionError при создании новых экземпляров.

В модуле unittest можно проверить (с assertRaises) конкретное исключение при вызове вызываемого, но, очевидно, это применимо к методам класса. Каков правильный способ запуска такого теста для конструктора?

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

Ответ 1

В модуле unittest можно проверить (с assertRaises) конкретное исключение при вызове вызываемого, но, очевидно, это применимо к методам класса. Каков правильный способ запуска такого теста для конструктора?

Конструктор сам является вызываемым:

self.assertRaises(AssertionError, MyClass, arg1, arg2)

Говоря об этом, я хочу повторить проблемы с nosklo и S.Lott относительно проверки типов аргументов. Кроме того, вы не должны использовать утверждения для проверки аргументов функции: утверждения наиболее полезны в качестве проверок здравомыслия, которые не будут вызваны, если что-то внутренне не соответствует вашему коду. Кроме того, утверждения assert компилируются, когда Python запускается в "оптимизированном" режиме -O. Если функция должна выполнять некоторую проверку своих аргументов, она должна создать правильное исключение.

Ответ 2

Не связывайтесь с assertRaises. Это слишком сложно.

Сделайте это

class Test_Init( unittest.TestCase ):
    def test_something( self ):
        try:
            x= Something( "This Should Fail" )
            self.fail( "Didn't raise AssertionError" )
        except AssertionError, e:
            self.assertEquals( "Expected Message", e.message )
            self.assertEquals( args, e.args )

Любое другое исключение будет обычной ошибкой теста.

Кроме того, не путайте слишком много ошибок перед проверкой в ​​методе __init__. Если кто-то предоставляет объект неправильного типа, ваш код будет терпеть неудачу в обычном ходе событий и нормальным образом вызвать нормальные средства. Вам не нужно "предварительно экранировать" объекты так много.

Ответ 3

Не знаю, помогает ли это, но проблема у меня была следующая:

self.assertRaises(Exception, MyFunction())

Проблема заключается в том, что я не просто передавал MyFunction, но и вызывал его, приводя к сбою и исключению. MyFunction ожидает аргумент, и я хочу, чтобы он потерпел неудачу, если ничего не прошло. Разозлило меня какое-то время, пока я не понял:

self.assertRaises(Exception, MyFunction)

Работает как ожидалось.

Ответ 4

Ну, в начале, проверка плохих аргументов - не очень хорошая идея в python. Python динамически силен для печати по какой-либо причине.

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

Вместо этого определите хороший API и хорошо документируйте его, используя docstrings и text, и оставьте ошибки плохих аргументов автоматически передаваемыми пользователю.

Пример:

def sum_two_values(value_a, value_b):
    return value_a + value_b

В порядке, этот пример глупо, но если я проверю и утвержу значение как целое, функция не будет работать с поплавками, строками, списком, без каких-либо причин, кроме моего чека, так зачем регистрироваться на первом месте? Он будет автоматически терпеть неудачу с типами, которые не будут работать, поэтому вам не придется беспокоиться.

Ответ 5

Ответ S.Lott неверен: сам self.fail() вызывает исключение, которое затем будет вызвано исключением в следующей строке:

class NetworkConfigTest1(unittest.TestCase):
    def runTest(self):
        try:
            NetworkConfig("192.168.256.0/24")
            self.fail("Exception expected but not thrown")
        except Exception, error:
            printf("Exception caught: %s" % str(error)
            pass

Выход был "Исключение ожидалось, но не выбрано", но unit test не был помечен как сбой, хотя проверенный код не был написан!

Более правильный способ проверить, вызывает ли метод исключение, будет использовать:

self.failUnlessRaises([error], [callable], [arguments to callable])

В моем случае тестируемый класс называется NetworkConfig, и конструктор должен выдать исключение, если сетевой дескриптор недействителен. И что получилось:

class NetworkConfigTest1(unittest.TestCase):
    def runTest(self):
        self.failUnlessRaises(Exception, NetworkConfig, "192.168.256.0/24")

Это работает по желанию и выполняет правильный тест.

Ответ 6

Если вы хотите только проверить, вызывает ли конструктор исключение, то лучше использовать лямбду:

    def testInsufficientArgs(self):
        self.assertRaises(ValueError, lambda: MyClass(0))

Таким образом, аргументы конструктора не устанавливаются "магически" (как в ответе @Miles), и среда IDE всегда может сказать вам, где используется конструктор.