3

I am trying to use python's unittest library to write some unit tests. I have a function that returns an unordered list of objects. I want to verify that the objects are the same, and I am trying to use assertCountEqual to do this.

However, this seems to be failing, despite the individual objects being equal (==) to each other. Here is the 'diff' output from the assertion failure:

First has 1, Second has 0:  Intersection(time=8.033252939677466e-08, del_time=8.033252939677466e-08, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
First has 1, Second has 0:  Intersection(time=-9.918729244820295e-16, del_time=-9.918729244820295e-16, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
First has 0, Second has 1:  Intersection(time=8.033252939677466e-08, del_time=8.033252939677466e-08, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
First has 0, Second has 1:  Intersection(time=-9.918729244820295e-16, del_time=-9.918729244820295e-16, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)

Verifying that they are equal:

>>> i = Intersection(time=8.033252939677466e-08, del_time=8.033252939677466e-08, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
>>> j = Intersection(time=8.033252939677466e-08, del_time=8.033252939677466e-08, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
>>> i == j
True
>>> i = Intersection(time=-9.918729244820295e-16, del_time=-9.918729244820295e-16, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
>>> j = Intersection(time=-9.918729244820295e-16, del_time=-9.918729244820295e-16, pos=Vector(10.00, 0.00), line=Line(Vector(500.00, 0.00), Vector(0.00, 0.00)), ent=None, oth=None, invalid=False)
>>> i == j
True

My guess is that the assertCountEqual function is checking if the two have the same identity (e.g. i is j), rather than equality.

  • Is there a unittest function that will provide the same diff capabilities, but use equality comparison, rather than identity?
  • Alternatively, is there some way I can write a function that performs similarly to assertCountEqual?

EDIT: I am running python 3.2.2.

Casey Kuball
  • 7,717
  • 5
  • 38
  • 70
  • 1
    Are you sure you even want `assertCountEqual()`? that iterates over the sequences and checks to see if they have the equal elements. That's not the same thing as saying that the sequences are themselves equal. If you define `Intersection.__eq__`, you probably just want plain-old `assertEqual()` – SingleNegationElimination May 04 '12 at 20:57
  • @TokenMacGuy I don't know that all of the Intersections will always be in the same order in my implementation. I don't care about the order either. I have an unordered list of objects, and `assertCountEqual` will determine that the two lists contain the same set (and number) of objects. `assertEqual` would add an additional restriction to the order. – Casey Kuball May 05 '12 at 01:48

3 Answers3

5

you can look for yourself how the comparison is done:

as your Intersections are objects, they are hashable per default, but if you don't provide a suitable hash function (which you should do if you provide comparison methods) they will be considered different.

so, does your Intersection class fullfill the hash contract?

user590028
  • 11,364
  • 3
  • 40
  • 57
mata
  • 67,110
  • 10
  • 163
  • 162
  • Thanks for the advice, and for providing the code source! :) I provide comparison methods in order to sort them, but is there any particular reason it would be normal to provide a hash function for classes that are comparable? I will make my `Intersection` class hashable, and accept if there are no problems. – Casey Kuball May 04 '12 at 22:39
  • 2
    when two objects compare to be equal, they should return the same hash value. when adding objects to a set or to dict as key, they are accessed by their hash value, so if they return different values, they won't be considered equal. a hash function should be fast and likely to produce different values for differen objects. to get the general idea, have a look [here](http://effbot.org/zone/python-hash.htm) – mata May 05 '12 at 00:59
  • This was not the actual cause of the problem (it was actually an issue with the output of the float not being exactly equal). In order to write the test cases, I'm just going to manually design the scenarios so that I won't have to deal with the floating point issues. I have already done this so that I won't have any problems in the future, though. :) – Casey Kuball May 05 '12 at 04:17
1

When working with unordered lists I typically use this pattern (if you can)

In a class that extends TestCase

self.assertTrue(set(a) == set(b), 'The lists are not equal.')

I use set in this case because it allows for the comparison of unordered groups BUT if a has two objects that are the same the comparison should fail but won't in that case you need to sort both lists and then compare.

I try to stay away from is except when comparing it to None because it relies on an instance like this

Here is an example

In [2]: a = [0,1,2]

In [3]: b = [0,2,1,0]

In [4]: set(a) == set(b)
Out[4]: True

In [5]: c = [2,0,1]

In [6]: a.sort() == c.sort()
Out[6]: True

For a more complex object or class you may want to try something like

self.assertTrue(a==b)

Or you could write your own compare method

def compare_complex(*args): 
  for attr in ...
    if getattr(args[0],attr) != getattr(args[1],attr): return False
  return True

I've used something similar in the past when analyzing two Classes that used attributes to store important values, or Numpy instances

Community
  • 1
  • 1
lukecampbell
  • 14,728
  • 4
  • 34
  • 32
  • `a.sort() == c.sort()` - the sort() method works in place and returns None, so this will always be true. You want `sorted(a) == sorted(c)`. – Thomas K May 04 '12 at 22:10
  • I wasn't sure what the underlying code did, I'm not using `is` on my own. My elements aren't (currently) hashable, which is why I didn't immediately think to use `set`. I supposed I could, now that I recently changed the users of `Intersection` to no longer change the instances. – Casey Kuball May 04 '12 at 22:36
  • @ThomasK I don't know for sure that sorting would work, as I have comparison done by the `time` attribute, and `__eq__` defined by comparing all of the individual attributes. Sorting may not be applicable here. – Casey Kuball May 04 '12 at 22:38
1

assertCountEqual() uses collections.Counter if your elements are hashable. In Python 3 if your class defines its own __eq__ then the default __hash__ is suppressed.

You have your own __eq__ -- define a __hash__ (it must be equal where __eq__ is equal) and you should be okay.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237