4

This question is not a duplicate of the above mentioned question, I'm not asking for a method to compare the instances, but about the method assertEqual,and what is it intended for.

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() failed?

Chris Martin
  • 29,484
  • 8
  • 71
  • 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

3 Answers3

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
  • 28,952
  • 5
  • 61
  • 78
  • 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

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

Quoting from official docs (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's no __eq__ magic method defined, therefore, it falls back to default implementation (they compare unequal unless they are the same object).

Question has nothing to do with unit test module.

Łukasz Rogalski
  • 20,566
  • 8
  • 54
  • 89
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