AssertRaises не удается, даже вызываемый вызывает требуемое исключение (python, unitest)

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

import unittest

# define a user-defined exception
class MyException(Exception):
    def __str__(self):
        return repr("ERROR: Just raised my exception!")

# this is my main class with a method raising this exception
class MyMainObject(object):

    def func(self):
        raise MyException()

# the test class
class TestConfig(unittest.TestCase):

    def test_1(self):

        other = MyMainObject()
        self.assertRaises(MyException, other.func())

# calling the test
if __name__ == '__main__':    
    unittest.main()

Когда other.func() вызывается в инструкции assert, MyException поднимается (можно легко проверить). Итак, тест assertRaises должен пройти тест, так как other.func() выходит с ошибкой MyException, НО:

....
MyException: 'ERROR: Just raised my exception!'

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

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

Ответ 1

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

Измените свой код на:

self.assertRaises(MyException, other.func)

и он будет работать правильно. Кроме того, вы можете использовать assertRaises в качестве менеджера контекста (python 2.7 и выше):

with self.assertRaises(MyException):
    other.func()

Использование assertRaises в качестве менеджера контекста имеет дополнительное преимущество: теперь вы можете получить экземпляр исключения и выполнить дополнительные тесты на нем:

with self.assertRaises(MyException) as raises_cm:
    other.func()

exception = raises_cm.exception
self.assertEqual(exception.args, ('foo', 'bar'))

Ответ 2

Из-за правил языков аргументы оцениваются до ввода кода вызываемой функции (и это вообще хорошо). Поэтому assertRaises не может перехватывать исключения, возникающие при оценке аргументов. Обходной путь (в нескольких API) заключается в том, что вы передаете вызываемые методы, такие как assertRaises, чтобы они могли оценить его в месте, где они могут контролировать, и где они могут перехватывать исключения. Если весь аргумент является вызовом метода, магия связанных методов позволяет вам сказать это довольно элегантно, без lambda или такой глупости:

self.assertRaises(MyException, other.func) # <- note, no parentheses after func