4

assertEqual(a, b) checks if a == b and return True or False,

The documentation says,

Test that first and second are equal. If the values do not compare equal, the test will fail.

I'm running three tests with assertEqual on a simple class,

The class on test

class Car:
    def __init__(self, name):
        self.name = name

The TestCase

class CarTest(unittest.TestCase):

    def test_diff_equal(self):
        car1 = Car('Ford')
        car2 = Car('Hyundai')
        self.assertEqual(car1, car2)

    def test_name_equal(self):
        car1 = Car('Ford')
        car2 = Car('Ford')
        self.assertEqual(car1, car2)

    def test_instance_equal(self):
        car1 = Car('Ford')
        self.assertEqual(car1, car1)

The results are

F.F
======================================================================
FAIL: test_diff_equal (cartest.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "cartest.py", line 10, in test_diff_equal
    self.assertEqual(car1, car2)
AssertionError: <car.Car instance at 0x7f499ec12ef0> != <car.Car instance at 0x7f499ec12f38>

======================================================================
FAIL: test_name_equal (cartest.CarTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "cartest.py", line 15, in test_name_equal
    self.assertEqual(car1, car2)
AssertionError: <car.Car instance at 0x7f499ec12fc8> != <car.Car instance at 0x7f499ec12f38>

----------------------------------------------------------------------
Ran 3 tests in 0.000s

FAILED (failures=2)

Is assertEqual used to check if both the instances are same? Or is anything wrong in my setup? Why did test_name_equal() fail?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • Possible duplicate of [Compare object instances for equality by their attributes in Python](http://stackoverflow.com/questions/1227121/compare-object-instances-for-equality-by-their-attributes-in-python) – Łukasz Rogalski Jan 11 '16 at 18:41
  • @Rogalski, Nope, don't dig older posts. It is SPECIFICALLY ABOUT assertEqual. –  Jan 11 '16 at 18:42
  • Which is strictly related (what you know, since you've quoted docs where it's stated) to how compared objects implements equality operator. – Łukasz Rogalski Jan 11 '16 at 18:46
  • @Rogalski This question is about the built in unittest module and the method it provide `assertEquals`, the mentioned question is about comparing two instances. How does these question relate? THIS QUESTION IS ABOUT UNITTEST.ASSERTEQUAL :: –  Jan 11 '16 at 18:51
  • @Rivadiz: Not exactly, your test fails because you didn't implement the `__eq__` method. – Vincent Savard Jan 11 '16 at 18:51
  • To know how `assertEquals` is implemented, [Use the Source, Luke](https://github.com/python/cpython/blob/master/Lib/unittest/case.py#L815). – Vincent Savard Jan 11 '16 at 19:00

5 Answers5

6

Your test is working absolutely fine, and it's found a bug. Hurray!

Your two Car objects may have the same name, but why would that mean that they are the same car? Nothing in your code makes that so.

If you want that to be the case, implement __eq__ on the Car class:

def __eq__(self, other):
    """Return True if other is also a car and has the same name as
    this one."""

    return isinstance(other, Car) and self.name == other.name

Then that test should pass.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
  • thank you for the answer, but what is `assertEqual` supposed to do? –  Jan 11 '16 at 18:44
  • @Rivadiz It'll raise an `AssertionError` if the two items are not equal. If they are, it will silently pass. – Elektito Jan 11 '16 at 18:51
  • so why does `test_name_equal()` failed? –  Jan 11 '16 at 18:53
  • @Rivadiz: The default `__eq__` method probably compares instances. – Vincent Savard Jan 11 '16 at 18:54
  • 2
    Because `car1` and `car2` are _not_ equal. They are two distinct objects. If you want them to be considered equal, you need to implement the `__eq__` special method as this answer suggests. – Elektito Jan 11 '16 at 18:54
  • 1
    @Rivadiz: it failed because in `test_name_equal`, `car1` and `car2` are _not equal_. They are two separate objects. They happen to have an attribute (`name`) that is equal, but that doesn't make them equal. – RemcoGerlich Jan 11 '16 at 20:45
1

The whole question is reducible to "How Python compare objects" what is precisely defined in Section 5.9: Comparisons of official documentation.

Quoting from the official documentation (emphasis mine) to clarify on aspects you're asking of.

Most other objects of built-in types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program.

That's what's covered by test_instance_equal and what essentially is:

o1 = object()
o1 == o1  # will always be True

The operators <, >, ==, >=, <=, and != compare the values of two objects. The objects need not have the same type. If both are numbers, they are converted to a common type. Otherwise, objects of different types always compare unequal, and are ordered consistently but arbitrarily. You can control comparison behavior of objects of non-built-in types by defining a cmp method or rich comparison methods like gt, described in section Special method names.*

Quoting from special method names:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

New in version 2.1.

These are the so-called “rich comparison” methods, and are called for comparison operators in preference to __cmp__() below. The correspondence between operator symbols and method names is as follows: (...) x==y calls x.__eq__(y), (...)

That's what test_diff_equal and test_name_equal shows. There isn't any __eq__ magic method defined, and therefore it falls back to the default implementation (they compare unequal unless they are the same object).

The question has nothing to do with unit testing a module.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Łukasz Rogalski
  • 22,092
  • 8
  • 59
  • 93
0

Adding to what's been already said: For the example given, you'll need to directly compare the attributes of the objects, and not the objects themselves, for unittest.TestCase.assertEqual to work.

class CarTest(unittest.TestCase):

    def test_diff_equal(self):
        car1 = Car('Ford')
        car2 = Car('Hyundai')
        self.assertEqual(car1.name, car2.name)

    def test_name_equal(self):
        car1 = Car('Ford')
        car2 = Car('Ford')
        self.assertEqual(car1.name, car2.name)

    def test_instance_equal(self):
        car1 = Car('Ford')
        self.assertEqual(car1.name, car1.name)

This should now work (and fail) as expected.

theMiNUS
  • 3
  • 1
0

Seems like a duplicate of:

Is there a way to check if two object contain the same values in each of their variables in python?

If you just want to check if the objects have the same instance variables you could write a unit test like

def test_name_equal(self):
        car1 = Car('Ford')
        car2 = Car('Ford')
        self.assertEqual(car1.__dict__, car2.__dict__) # will pass

or you could implement an equality function that does this like in the answer from the post I mentioned above and your original test will pass.
Note that this does not check if the objects are of the same class, see also the comments of the same above answer.

Naitzirch
  • 45
  • 1
  • 2
  • 6
0

TLDR

assertEqual(expected, actual) asserts that expected==actual. To understand what == means in python : read the documentation that explains the “rich comparison” methods. Here which interest you is object.__eq__(self, other).
Now, if you define your own class and you want compare objects between them by relying on their attributes and not the identity(reference) of the objects, you need to override __eq__(self, other) to mean how two objects should be compared.

Detailed answer

I don't understand why nobody answers to the exact question of the user, that is, what does the assertEqual() function ?

I stumbled across the same question today, so I'm looking for in stackoverflow, but not found. Here it is the answer to the question:

  • Generally, a public function of a library documents what the function is supposed to do. We can read in the documentation:

Test that first and second are equal. If the values do not compare equal, the test will fail.

In addition, if first and second are the exact same type and one of list, tuple, dict, set, frozenset or str or any type that a subclass registers with addTypeEqualityFunc() the type-specific equality function will be called in order to generate a more useful default error message (see also the list of type-specific methods).

Changed in version 3.1: Added the automatic calling of type-specific equality function.

Changed in version 3.2: assertMultiLineEqual() added as the default type equality function for comparing strings.

  • In case of doubt about the documentation or your specific version of unittest, you can look in the code :

We have :

def assertEqual(self, first, second, msg=None):
    """Fail if the two objects are unequal as determined by the '=='
       operator.
    """
    assertion_func = self._getAssertEqualityFunc(first, second)
    assertion_func(first, second, msg=msg)

that relies on :

def _getAssertEqualityFunc(self, first, second):
    """Get a detailed comparison function for the types of the two args.

    Returns: A callable accepting (first, second, msg=None) that will
    raise a failure exception if first != second with a useful human
    readable error message for those types.
    """
    if type(first) is type(second):
        asserter = self._type_equality_funcs.get(type(first))
        if asserter is not None:
            if isinstance(asserter, str):
                asserter = getattr(self, asserter)
            return asserter

    return self._baseAssertEqual

that relies on (the important part) :

def _baseAssertEqual(self, first, second, msg=None):
    """The default assertEqual implementation, not type specific."""
    if not first == second:
        standardMsg = '%s != %s' % _common_shorten_repr(first, second)
        msg = self._formatMessage(msg, standardMsg)
        raise self.failureException(msg)
davidxxx
  • 125,838
  • 23
  • 214
  • 215