Search code examples
pythonassertpython-unittest

How to use `assertEqual()` [or equivalent] without much baggage?


I am looking for a method (if available) that can compare two values and raise an assertion error with a meaningful message when the comparison fails.

If I use assert, the failure message does not contain what values were compared when then assertion failed.

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

If I use the assertEqual() method from the unittest.TestCase package, the assertion message contains the values that were compared.

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

Note that, here, the assertion error message contains the values that were compared. That is very useful in real-life scenarios and hence necessary for me. The plain assert (see above) does not do that.

However, so far, I could use assertEqual() only in the class that inherits from unittest.TestCase and provides few other required methods like runTest(). I want to use assertEqual() anywhere, not only in the inherited classes. Is that possible?

I tried the following but they did not work.

>>> 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
>>> 

Is there any other package or library that offers similar methods like assertEqual() that can be easily used without additional constraints?


Solution

  • It is possible to create a "helper" new module that provides access to the assert functions. AssertsAccessor in this case:

    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
    

    The module needs to be created just once, and then all asserts from the unittest package are accessible, e.g.:

    AssertsAccessor.assertEquals(1, 2)
    

    AssertionError: 1 != 2

    Or another example:

    AssertsAccessor.assertGreater(1, 2)
    

    Which results:

    AssertionError: 1 not greater than 2

    Assuming the module created for the accessor is named assertions, the common usage in the code would look like:

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