Как использовать assertEqual () [или эквивалент] без лишнего багажа?

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

Если я использую assert, в сообщении об ошибке не будет указано, какие значения сравнивались, когда утверждение не удалось.

>>> a = 3
>>> b = 4
>>> assert a == b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>> 

Если я использую метод assertEqual() из пакета unittest.TestCase, сообщение подтверждения будет содержать значения, которые сравнивались.

        a = 3
        b = 4
>       self.assertEqual(a, b)
E       AssertionError: 3 != 4

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

Однако до сих пор я мог использовать assertEqual() только в классе, который наследуется от unittest.TestCase и предоставляет несколько других необходимых методов, таких как runTest(). Я хочу использовать assertEqual() где угодно, а не только в унаследованных классах. Это возможно?

Я пробовал следующее, но они не работали.

>>> import unittest
>>> unittest.TestCase.assertEqual(a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method failUnlessEqual() must be called with TestCase instance as first argument (got int instance instead)
>>> 
>>> 
>>> tc = unittest.TestCase()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.6/unittest.py", line 215, in __init__
    (self.__class__, methodName)
ValueError: no such test method in <class 'unittest.TestCase'>: runTest
>>> 

Есть ли какой-либо другой пакет или библиотека, предлагающая аналогичные методы, такие как assertEqual(), которые можно легко использовать без дополнительных ограничений?

unittest.TestCase().assertEqual(a, b) пробовали?

Abdul Niyas P M 20.10.2018 17:20

@AbdulNiyasPM: Тоже не работает. Та же ошибка: ValueError: no such test method in <class 'unittest.TestCase'>: runTest

Arun 20.10.2018 17:21
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
2
3 011
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вы должны передать сообщение утверждения вручную:

assert a == b, '%s != %s' % (a, b)
# AssertionError: 3 != 4

Да, именно так. Однако это очень много лишнего набора :) Фактически, дополнительного набора текста без добавления дополнительной "информации" в исходный код. Если ничего не выходит, то я подумываю создать свою крошечную библиотеку для такой упаковки :).

Arun 20.10.2018 17:24

вы смотрели numpy.testing?

https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.testing.html

Среди прочего в нем есть: assert_almost_equal (фактический, желаемый [, ...]) Вызывает ошибку AssertionError, если два элемента не равны с требуемой точностью.

Это assert выводит на печать фактическое и желаемое. Если вы увеличиваете точность, сравнение будет произвольно близко к == (для чисел с плавающей запятой)

Интересно. numpy.testing раньше не проверял. У них тоже есть assert_equal()! Однако у них нет assertXxx (), предлагаемого unittest.TestCase.

Arun 20.10.2018 17:28
Ответ принят как подходящий

Можно создать новый модуль «помощник», который предоставляет доступ к функциям утверждения. AssertsAccessor в данном случае:

from unittest import TestCase

# Dummy TestCase instance, so we can initialize an instance
# and access the assert instance methods
class DummyTestCase(TestCase):
    def __init__(self):
        super(DummyTestCase, self).__init__('_dummy')

    def _dummy(self):
        pass

# A metaclass that makes __getattr__ static
class AssertsAccessorType(type):
    dummy = DummyTestCase()

    def __getattr__(cls, key):
        return getattr(AssertsAccessor.dummy, key)

# The actual accessor, a static class, that redirect the asserts
class AssertsAccessor(object):
    __metaclass__ = AssertsAccessorType

Модуль нужно создать только один раз, и тогда будут доступны все утверждает из пакета unittest, например:

AssertsAccessor.assertEquals(1, 2)

AssertionError: 1 != 2

Или другой пример:

AssertsAccessor.assertGreater(1, 2)

Какие результаты:

AssertionError: 1 not greater than 2

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

from assertions import AssertsAccessor

def foo(small_arg, big_arg):
    AssertsAccessor.assertGreater(big_arg, small_arg)
    # some logic here

У меня было подобное в прошлом, и в конце концов я написал короткое настраиваемое утверждение, которое принимает любое условие в качестве ввода.

import inspect

def custom_assert(condition):
    if not condition:
        frame = inspect.currentframe()
        frame = inspect.getouterframes(frame)[1]
        call_signature = inspect.getframeinfo(frame[0]).code_context[0].strip()

        import re
        argument = re.search('\((.+)\)', call_signature).group(1)
        if '!=' in argument:
            argument = argument.replace('!=','==')
        elif '==' in argument:
            argument = argument.replace('==','!=')
        elif '<' in argument:
            argument = argument.replace('<','>=')
        elif '>' in argument:
            argument = argument.replace('>','<=')
        elif '>=' in argument:
            argument = argument.replace('>=','<')
        elif '<=' in argument:
            argument = argument.replace('<=','>')

        raise AssertionError(argument)

if __name__ == '__main__':
    custom_assert(2 == 1)

Выход:

Traceback (most recent call last):
  File "custom_assert.py", line 27, in <module>
    custom_assert(2 == 1)
  File "custom_assert.py", line 24, in custom_assert
    raise AssertionError(argument)
AssertionError: 2 != 1

Это тоже интересная идея. Спасибо. Добро пожаловать в SO.

Arun 20.10.2018 18:00

AssertEqual или любой другой метод assertXxx () ожидает, что первый аргумент будет ссылкой на объект. Обычно мы называем этот метод self.assertEqual(first, second, msg=None). Здесь self удовлетворяет первому ожидаемому аргументу. Чтобы обойти эту ситуацию, мы можем сделать следующее:

from unittest import TestCase as tc
def some_func():
    dummy_obj = tc()
    tc.assertEqual(dummy_obj, 123, 123, msg='Not Equal')

Причина такого поведения - похмелье от фреймворков XUnit.

Другие вопросы по теме