Assert that two lists of objects are equal in django testing
Asked Answered
Z

2

12

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?

Zoilazoilla answered 2/6, 2014 at 4:8 Comment(2)
The reason the zip() works is because the text representation of your tag is the slug, and that's what you are comparing with.Artie
Why wouldn't comparing the arrays do the same? I'll edit my question to show an exampleZoilazoilla
Z
17

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

Zoilazoilla answered 4/6, 2014 at 4:12 Comment(0)
A
3

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.

Artie answered 2/6, 2014 at 4:29 Comment(3)
hmm values_list is a good trick to know, +1 for that. I agree, in this case this is a useless test. I just wanted a simple example to show the test failing. My actual test isn't this trivial. I'd prefer not to use slugs because I want to check that the actual models are equal, but I suppose that it could be done with their pksZoilazoilla
Don't use pk because those are incremental (most likely) and two primary keys would not be equal (they shouldn't, if they are you have bigger problems). If the desired functionality is to prevent duplicates, then use unique_together to help enforce this at the database level.Artie
Can you expand on how to use unique_together in this case? It's not clear to me.Zoilazoilla

© 2022 - 2024 — McMap. All rights reserved.