Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assert that two lists of objects are equal in django testing

Is there a way to check that two lists of objects are equal in django tests.

lets say I have some model:

class Tag(models.Model):
    slug = models.SlugField(max_length=50, unique=True)
    def __unicode__(self):
        return self.slug

and I run this simple test:

def test_equal_list_fail(self):
    tag_list = []
    for tag in ['a', 'b', 'c']:
        tag_list.append(Tag.objects.create(slug=tag))

    tags = Tag.objects.all()

    self.assertEqual(tag_list, tags)

this fails with:

======================================================================
FAIL: test_equal_list_fail (userAccount.tests.ProfileTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "path/to/tests.py", line 155, in test_equal_list_fail
    self.assertEqual(tag_list, tags)
AssertionError: [<Tag: a>, <Tag: b>, <Tag: c>] != [<Tag: a>, <Tag: b>, <Tag: c>]

----------------------------------------------------------------------

this will work:

def test_equal_list_passes(self):
    tag_list = []
    for tag in ['a', 'b', 'c']:
        tag_list.append(Tag.objects.create(slug=tag))

    tags = Tag.objects.all()

    for tag_set in zip(tags, tag_list):
        self.assertEqual(*tag_set)

However, This fails:

def test_equal_list_fail(self):
    tag_list = []
    for tag in ['a', 'b', 'c']:
        tag_list.append(Tag.objects.create(slug=tag))

    tags = Tag.objects.all()

    for tag_set in zip(tags, tag_list):
        print "\n"
        print tag_set[0].slug + "'s pk is %s" % tag_set[0].pk
        print tag_set[1].slug + "'s pk is %s" % tag_set[1].pk
        print "\n"
        self.assertIs(*tag_set)

with:

Creating test database for alias 'default'...
.......

a's pk is 1
a's pk is 1

F.
======================================================================
FAIL: test_equal_list_fail (userAccount.tests.ProfileTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "path/to/tests.py", line 160, in test_equal_list_fail
    self.assertIs(*tag_set)
AssertionError: <Tag: a> is not <Tag: a>

Is this expected behavior?

Edit in response to comment
This type of comparison works:

class Obj:
    def __init__(self, x):
        self.x = x

>>> one = Obj(1)
>>> two = Obj(2)
>>> a = [one, two]
>>> b = [one, two]
>>> a == b
True

Why is the test failing for the other arrays?

like image 302
Ben Avatar asked Jun 02 '14 04:06

Ben


2 Answers

To test two lists

use: assertSequenceEqual

Because, in this case, tags = Tag.objects.all() generates a django.db.models.query.QuerySet where as tag_list.append(...) creates a list.

Other options in different situations are:

  • assertListEqual(a, b)
  • assertTupleEqual(a, b)
  • assertSetEqual(a, b)
  • assertDictEqual(a, b)

Why <Tag: a> is not <Tag: a>

The tags are the same model, but they've been loaded into different places in memory

for tag_set in zip(tags, tag_list):
    print "\n"
    print tag_set[0].slug + "'s pk is %s" % tag_set[0].pk + ' id is: ' + id(tag_set[0])
    print tag_set[1].slug + "'s pk is %s" % tag_set[1].pk + ' id is: ' + id(tag_set[1])
    print "\n"
    self.assertIs(*tag_set)

returns

.......

a's pk is 1 id is: 4522000208
a's pk is 1 id is: 4522228112

F.

Therefore, is will retrun False

like image 109
Ben Avatar answered Nov 08 '22 16:11

Ben


I think what you want to test is if the tags created have the same slugs as those in your test list.

For that, fetch only the slug as a list with values_list, and then compare that:

assertEqual(Tag.objects.values_list('slug', flat=True), ['a','b','c'])

I have to say, this isn't quite a useful test because you are checking django orm functionality, which has already been tested quite well.

Your tests should check for specifics of your own application.

like image 38
Burhan Khalid Avatar answered Nov 08 '22 17:11

Burhan Khalid