Подтвердить что ты не робот

Как показать сообщения об ошибках, обнаруженные assertRaises() в unittest в Python2.7?

Чтобы сообщения об ошибках из моего модуля были информативными, я хотел бы видеть все сообщения об ошибках, обнаруженные assertRaises(). Сегодня я делаю это для каждого assertRaises(), но поскольку их в тестовом коде очень много, он становится очень утомительным.

Как распечатать сообщения об ошибках для всех assertRaises()? Я изучил документацию на http://docs.python.org/library/unittest.html, не выясняя, как ее решить. Могу ли я каким-либо образом обезвредить метод assertRaises()? Я предпочитаю не изменять все строки assertRaises() в тестовом коде, так как я чаще всего использую тестовый код стандартным способом.

Я думаю, этот вопрос связан с Python unittest: как я могу проверить аргумент в Exceptions?

Вот как я это делаю сегодня. Например:

#!/usr/bin/env python

def fail():
    raise ValueError('Misspellled errrorr messageee')

И тестовый код:

#!/usr/bin/env python
import unittest
import failure   

class TestFailureModule(unittest.TestCase):

    def testFail(self):
        self.assertRaises(ValueError, failure.fail)

if __name__ == '__main__':
    unittest.main()  

Чтобы проверить сообщение об ошибке, я просто изменяю тип ошибки в assertRaises(), например, на IOError. Затем я вижу сообщение об ошибке:

 E
======================================================================
ERROR: testFail (__main__.TestFailureModule)
----------------------------------------------------------------------
Traceback (most recent call last):
 File "test_failure.py", line 8, in testFail
   self.assertRaises(IOError, failure.fail)
  File "/usr/lib/python2.7/unittest/case.py", line 471, in assertRaises
    callableObj(*args, **kwargs)
 File "/home/jonas/Skrivbord/failure.py", line 4, in fail
    raise ValueError('Misspellled errrorr messageee')
ValueError: Misspellled errrorr messageee

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

FAILED (errors=1)

Любые предложения? /Джонас

EDIT:

С подсказками Роберта Россни мне удалось решить проблему. Он не предназначен главным образом для орфографических ошибок, но для обеспечения того, чтобы сообщения об ошибках были действительно значимыми для пользователя модуля. Обычная функциональность unittest (именно так я использую ее большую часть времени) достигается установкой SHOW_ERROR_MESSAGES = False.

Я просто переопределяю метод assertRaises(), как показано ниже. Он работает как шарм!

SHOW_ERROR_MESSAGES = True

class NonexistantError(Exception):
    pass

class ExtendedTestCase(unittest.TestCase):
    def assertRaises(self, excClass, callableObj, *args, **kwargs):
        if SHOW_ERROR_MESSAGES:
            excClass = NonexistantError
        try:
            unittest.TestCase.assertRaises(self, excClass, callableObj, *args, **kwargs)
        except:
            print '\n    ' + repr(sys.exc_info()[1]) 

Доля полученного результата:

testNotIntegerInput (__main__.TestCheckRegisteraddress) ... 
    TypeError('The registeraddress must be an integer. Given: 1.0',)

    TypeError("The registeraddress must be an integer. Given: '1'",)

    TypeError('The registeraddress must be an integer. Given: [1]',)

    TypeError('The registeraddress must be an integer. Given: None',)
ok
testCorrectNumberOfBytes (__main__.TestCheckResponseNumberOfBytes) ... ok
testInconsistentLimits (__main__.TestCheckNumerical) ... 
    ValueError('The maxvalue must not be smaller than minvalue. Given: 45 and 47, respectively.',)

    ValueError('The maxvalue must not be smaller than minvalue. Given: 45.0 and 47.0, respectively.',)
ok
testWrongValues (__main__.TestCheckRegisteraddress) ... 
    ValueError('The registeraddress is too small: -1, but minimum value is 0.',)

    ValueError('The registeraddress is too large: 65536, but maximum value is 65535.',)
ok
testTooShortString (__main__.TestCheckResponseWriteData) ... 
    ValueError("The payload is too short: 2, but minimum value is 4. Given: '\\x00X'",)

    ValueError("The payload is too short: 0, but minimum value is 4. Given: ''",)

    ValueError("The writedata is too short: 1, but minimum value is 2. Given: 'X'",)

    ValueError("The writedata is too short: 0, but minimum value is 2. Given: ''",)
ok
testKnownValues (__main__.TestCreateBitPattern) ... ok
testNotIntegerInput (__main__.TestCheckSlaveaddress) ... 
    TypeError('The slaveaddress must be an integer. Given: 1.0',)

    TypeError("The slaveaddress must be an integer. Given: '1'",)

    TypeError('The slaveaddress must be an integer. Given: [1]',)

    TypeError('The slaveaddress must be an integer. Given: None',)
ok
4b9b3361

Ответ 1

Вне коробки unittest этого не делает. Если вы часто это делаете, вы можете попробовать что-то вроде этого:

class ExtendedTestCase(unittest.TestCase):

  def assertRaisesWithMessage(self, msg, func, *args, **kwargs):
    try:
      func(*args, **kwargs)
      self.assertFail()
    except Exception as inst:
      self.assertEqual(inst.message, msg)

Выведите классы unit test из ExtendedTestCase вместо unittest.TestCase.

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

Ответ 2

Я когда-то предпочел самый отличный ответ, приведенный выше @Robert Rossney. В настоящее время я предпочитаю использовать assertRaises в качестве менеджера контекста (новая возможность в unittest2), например:

with self.assertRaises(TypeError) as cm:
    failure.fail()
self.assertEqual(
    'The registeraddress must be an integer. Given: 1.0',
    str(cm.exception)
)

Ответ 3

Вы ищете assertRaisesRegexp, который доступен с Python 2.7. Из документов:

self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$', int, 'XYZ')

или:

with self.assertRaisesRegexp(ValueError, 'literal'):
    int('XYZ')

Ответ 4

Если сообщение об ошибке точно соответствует:

with self.assertRaises(ValueError) as error:
  do_something()
self.assertEqual(error.exception.message, 'error message')

Ответ 5

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

Итак, я хотел бы улучшить ответ Роберта Россни:

class TestCaseMixin(object):

    def assertRaisesWithMessage(self, exception_type, message, func, *args, **kwargs):
        try:
            func(*args, **kwargs)
        except exception_type as e:
            self.assertEqual(e.args[0], message)
        else:
            self.fail('"{0}" was expected to throw "{1}" exception'
                      .format(func.__name__, exception_type.__name__))

Основные отличия:

  • Мы проверяем тип исключения.
  • Мы можем запустить этот код как на Python 2, так и на Python 3 (мы называем e.args[0], потому что ошибки в Py3 не имеют message).