diff --git a/django/test/testcases.py b/django/test/testcases.py index a4edf4929a..58e316ac39 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from collections import Counter from copy import copy import difflib import errno @@ -871,7 +872,7 @@ class TransactionTestCase(SimpleTestCase): def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True, msg=None): items = six.moves.map(transform, qs) if not ordered: - return self.assertEqual(set(items), set(values), msg=msg) + return self.assertEqual(Counter(items), Counter(values), msg=msg) values = list(values) # For example qs.iterator() could be passed as qs, but it does not # have 'ordered' attribute. diff --git a/tests/test_utils/models.py b/tests/test_utils/models.py index 4c6ee0d19a..b4ce172b27 100644 --- a/tests/test_utils/models.py +++ b/tests/test_utils/models.py @@ -1,10 +1,25 @@ from django.db import models from django.utils.encoding import python_2_unicode_compatible - @python_2_unicode_compatible -class Person(models.Model): +class Car(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name + +@python_2_unicode_compatible +class Person(models.Model): + name = models.CharField(max_length=100) + cars = models.ManyToManyField(Car, through='PossessedCar') + + def __str__(self): + return self.name + +@python_2_unicode_compatible +class PossessedCar(models.Model): + car = models.ForeignKey(Car) + belongs_to = models.ForeignKey(Person) + + def __str__(self): + return self.color diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 8cd0019f7b..ef3114cd16 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -14,7 +14,7 @@ from django.test.html import HTMLParseError, parse_html from django.test.utils import CaptureQueriesContext, override_settings from django.utils import six -from .models import Person +from .models import Car, Person, PossessedCar from .views import empty_response @@ -178,6 +178,33 @@ class AssertQuerysetEqualTests(TestCase): [repr(self.p1)] ) + def test_repeated_values(self): + """ + Test that assertQuerysetEqual checks the number of appearance of each item + when used with option ordered=False. + """ + batmobile = Car.objects.create(name='Batmobile') + k2000 = Car.objects.create(name='K 2000') + PossessedCar.objects.bulk_create([ + PossessedCar(car=batmobile, belongs_to=self.p1), + PossessedCar(car=batmobile, belongs_to=self.p1), + PossessedCar(car=k2000, belongs_to=self.p1), + PossessedCar(car=k2000, belongs_to=self.p1), + PossessedCar(car=k2000, belongs_to=self.p1), + PossessedCar(car=k2000, belongs_to=self.p1), + ]) + with self.assertRaises(AssertionError): + self.assertQuerysetEqual( + self.p1.cars.all(), + [repr(batmobile), repr(k2000)], + ordered=False + ) + self.assertQuerysetEqual( + self.p1.cars.all(), + [repr(batmobile)] * 2 + [repr(k2000)] * 4, + ordered=False + ) + @override_settings(ROOT_URLCONF='test_utils.urls') class CaptureQueriesContextManagerTests(TestCase):