Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions django/test/testcases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,11 +1040,29 @@ def _fixture_teardown(self):
allow_cascade=self.available_apps is not None,
inhibit_post_migrate=inhibit_post_migrate)

def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True, msg=None):
items = map(transform, qs)
def assertQuerysetEqual(self, qs, values, transform=None, ordered=True, msg=None):
values = list(values)
# RemovedInDjango41Warning.
if transform is None:
if (
values and isinstance(values[0], str) and
qs and not isinstance(qs[0], str)
):
# Transform qs using repr() if the first element of values is a
# string and the first element of qs is not (which would be the
# case if qs is a flattened values_list).
warnings.warn(
"In Django 4.1, repr() will not be called automatically "
"on a queryset when compared to string values. Set an "
"explicit 'transform' to silence this warning.",
category=RemovedInDjango41Warning,
)
transform = repr
items = qs
if transform is not None:
items = map(transform, items)
if not ordered:
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.
if len(values) > 1 and hasattr(qs, 'ordered') and not qs.ordered:
Expand Down
3 changes: 3 additions & 0 deletions docs/internals/deprecation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ details on these changes.

* The ``default_app_config`` module variable will be removed.

* ``TransactionTestCase.assertQuerysetEqual()` will no longer automatically
call ``repr()`` on a queryset when compared to string values.

.. _deprecation-removed-in-4.0:

4.0
Expand Down
14 changes: 7 additions & 7 deletions docs/intro/tutorial05.txt
Original file line number Diff line number Diff line change
Expand Up @@ -498,11 +498,11 @@ class:
Questions with a pub_date in the past are displayed on the
index page.
"""
create_question(question_text="Past question.", days=-30)
question = create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
[question],
)

def test_future_question(self):
Expand All @@ -520,24 +520,24 @@ class:
Even if both past and future questions exist, only past questions
are displayed.
"""
create_question(question_text="Past question.", days=-30)
question = create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
[question],
)

def test_two_past_questions(self):
"""
The questions index page may display multiple questions.
"""
create_question(question_text="Past question 1.", days=-30)
create_question(question_text="Past question 2.", days=-5)
question1 = create_question(question_text="Past question 1.", days=-30)
question2 = create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question 2.>', '<Question: Past question 1.>']
[question2, question1],
)


Expand Down
10 changes: 10 additions & 0 deletions docs/releases/3.2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,11 @@ Tests
<django.db.transaction.on_commit>` in a list. This allows you to test such
callbacks without using the slower :class:`.TransactionTestCase`.

* :meth:`.TransactionTestCase.assertQuerysetEqual` now supports direct
comparison against another queryset rather than being restricted to
comparison against a list of string representations of objects when using the
default value for the ``transform`` argument.

URLs
~~~~

Expand Down Expand Up @@ -615,3 +620,8 @@ Miscellaneous
* The ``default_app_config`` application configuration variable is deprecated,
due to the now automatic ``AppConfig`` discovery. See :ref:`whats-new-3.2`
for more details.

* Automatically calling ``repr()`` on a queryset in
``TransactionTestCase.assertQuerysetEqual()``, when compared to string
values, is deprecated. If you need the previous behavior, explicitly set
``transform`` to ``repr``.
27 changes: 20 additions & 7 deletions docs/topics/testing/tools.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1683,15 +1683,13 @@ your test suite.

Output in case of error can be customized with the ``msg`` argument.

.. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True, msg=None)
.. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=None, ordered=True, msg=None)

Asserts that a queryset ``qs`` returns a particular list of values ``values``.
Asserts that a queryset ``qs`` matches a particular iterable of values
``values``.

The comparison of the contents of ``qs`` and ``values`` is performed by
applying ``transform`` to ``qs``. By default, this means that the
``repr()`` of each value in ``qs`` is compared to the ``values``. Any other
callable can be used if ``repr()`` doesn't provide a unique or helpful
comparison.
If ``transform`` is provided, ``values`` is compared to a list produced by
applying ``transform`` to each member of ``qs``.

By default, the comparison is also ordering dependent. If ``qs`` doesn't
provide an implicit ordering, you can set the ``ordered`` parameter to
Expand All @@ -1702,6 +1700,21 @@ your test suite.

Output in case of error can be customized with the ``msg`` argument.

.. versionchanged:: 3.2

The default value of ``transform`` argument was changed to ``None``.

.. versionadded:: 3.2

Support for direct comparison between querysets was added.

.. deprecated:: 3.2

If ``transform`` is not provided and ``values`` is a list of strings,
it's compared to a list produced by applying ``repr()`` to each member
of ``qs``. This behavior is deprecated and will be removed in Django
4.1. If you need it, explicitly set ``transform`` to ``repr``.

.. method:: TransactionTestCase.assertNumQueries(num, func, *args, **kwargs)

Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that
Expand Down
12 changes: 6 additions & 6 deletions tests/aggregation/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,13 +750,13 @@ def test_dates_with_aggregation(self):
number of authors.
"""
dates = Book.objects.annotate(num_authors=Count("authors")).dates('pubdate', 'year')
self.assertQuerysetEqual(
self.assertSequenceEqual(
dates, [
"datetime.date(1991, 1, 1)",
"datetime.date(1995, 1, 1)",
"datetime.date(2007, 1, 1)",
"datetime.date(2008, 1, 1)"
]
datetime.date(1991, 1, 1),
datetime.date(1995, 1, 1),
datetime.date(2007, 1, 1),
datetime.date(2008, 1, 1),
],
)

def test_values_aggregation(self):
Expand Down
36 changes: 17 additions & 19 deletions tests/basic/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,11 @@ def test_queryset_delete_removes_all_items_in_that_queryset(self):
Article(headline=headline, pub_date=some_pub_date).save()
self.assertQuerysetEqual(
Article.objects.all().order_by('headline'),
["<Article: Amazing article>",
"<Article: An article>",
"<Article: Article One>",
"<Article: Boring article>"]
sorted(headlines),
transform=lambda a: a.headline,
)
Article.objects.filter(headline__startswith='A').delete()
self.assertQuerysetEqual(Article.objects.all().order_by('headline'), ["<Article: Boring article>"])
self.assertEqual(Article.objects.get().headline, 'Boring article')

def test_not_equal_and_equal_operators_behave_as_expected_on_instances(self):
some_pub_date = datetime(2014, 5, 16, 12, 1)
Expand Down Expand Up @@ -208,17 +206,17 @@ def test_create_method(self):
def test_year_lookup_edge_case(self):
# Edge-case test: A year lookup should retrieve all objects in
# the given year, including Jan. 1 and Dec. 31.
Article.objects.create(
a11 = Article.objects.create(
headline='Article 11',
pub_date=datetime(2008, 1, 1),
)
Article.objects.create(
a12 = Article.objects.create(
headline='Article 12',
pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999),
)
self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.filter(pub_date__year=2008),
["<Article: Article 11>", "<Article: Article 12>"]
[a11, a12],
)

def test_unicode_data(self):
Expand Down Expand Up @@ -442,7 +440,7 @@ def test_all_lookup(self):
self.a.save()

# Article.objects.all() returns all the articles in the database.
self.assertQuerysetEqual(Article.objects.all(), ['<Article: Parrot programs in Python>'])
self.assertSequenceEqual(Article.objects.all(), [self.a])

def test_rich_lookup(self):
# Django provides a rich database lookup API.
Expand All @@ -458,24 +456,24 @@ def test_equal_lookup(self):
self.assertEqual(Article.objects.get(id=self.a.id), self.a)
self.assertEqual(Article.objects.get(headline='Swallow programs in Python'), self.a)

self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.filter(pub_date__year=2005),
['<Article: Swallow programs in Python>'],
[self.a],
)
self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.filter(pub_date__year=2004),
[],
)
self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.filter(pub_date__year=2005, pub_date__month=7),
['<Article: Swallow programs in Python>'],
[self.a],
)

self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.filter(pub_date__week_day=5),
['<Article: Swallow programs in Python>'],
[self.a],
)
self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.filter(pub_date__week_day=6),
[],
)
Expand All @@ -499,7 +497,7 @@ def test_lookup_by_primary_key(self):
self.assertEqual(Article.objects.get(pk=self.a.id), self.a)

# pk can be used as a shortcut for the primary key name in any query.
self.assertQuerysetEqual(Article.objects.filter(pk__in=[self.a.id]), ["<Article: Swallow programs in Python>"])
self.assertSequenceEqual(Article.objects.filter(pk__in=[self.a.id]), [self.a])

# Model instances of the same type and same ID are considered equal.
a = Article.objects.get(pk=self.a.id)
Expand Down
21 changes: 9 additions & 12 deletions tests/custom_columns/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ def test_get_author_m2m_relation(self):
)

def test_author_querying(self):
self.assertQuerysetEqual(
self.assertSequenceEqual(
Author.objects.all().order_by('last_name'),
['<Author: Peter Jones>', '<Author: John Smith>']
[self.a2, self.a1],
)

def test_author_filtering(self):
self.assertQuerysetEqual(
self.assertSequenceEqual(
Author.objects.filter(first_name__exact='John'),
['<Author: John Smith>']
[self.a1],
)

def test_author_get(self):
Expand All @@ -111,15 +111,12 @@ def test_author_get_attributes(self):
getattr(a, 'last')

def test_m2m_table(self):
self.assertQuerysetEqual(
self.assertSequenceEqual(
self.article.authors.all().order_by('last_name'),
['<Author: Peter Jones>', '<Author: John Smith>']
[self.a2, self.a1],
)
self.assertQuerysetEqual(
self.a1.article_set.all(),
['<Article: Django lets you build Web apps easily>']
)
self.assertQuerysetEqual(
self.assertSequenceEqual(self.a1.article_set.all(), [self.article])
self.assertSequenceEqual(
self.article.authors.filter(last_name='Jones'),
['<Author: Peter Jones>']
[self.a2],
)
18 changes: 9 additions & 9 deletions tests/custom_lookups/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,17 +308,17 @@ class BilateralTransformTests(TestCase):

def test_bilateral_upper(self):
with register_lookup(models.CharField, UpperBilateralTransform):
Author.objects.bulk_create([
Author(name='Doe'),
Author(name='doe'),
Author(name='Foo'),
])
self.assertQuerysetEqual(
author1 = Author.objects.create(name='Doe')
author2 = Author.objects.create(name='doe')
author3 = Author.objects.create(name='Foo')
self.assertCountEqual(
Author.objects.filter(name__upper='doe'),
["<Author: Doe>", "<Author: doe>"], ordered=False)
self.assertQuerysetEqual(
[author1, author2],
)
self.assertSequenceEqual(
Author.objects.filter(name__upper__contains='f'),
["<Author: Foo>"], ordered=False)
[author3],
)

def test_bilateral_inner_qs(self):
with register_lookup(models.CharField, UpperBilateralTransform):
Expand Down
53 changes: 31 additions & 22 deletions tests/datetimes/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,33 +134,42 @@ def test_datetimes_returns_available_dates_for_given_scope_and_given_field(self)
for i, pub_date in enumerate(pub_dates):
Article(pub_date=pub_date, title='title #{}'.format(i)).save()

self.assertQuerysetEqual(
self.assertSequenceEqual(
Article.objects.datetimes('pub_date', 'year'),
["datetime.datetime(2005, 1, 1, 0, 0)"])
self.assertQuerysetEqual(
[datetime.datetime(2005, 1, 1, 0, 0)],
)
self.assertSequenceEqual(
Article.objects.datetimes('pub_date', 'month'),
["datetime.datetime(2005, 7, 1, 0, 0)"])
self.assertQuerysetEqual(
[datetime.datetime(2005, 7, 1, 0, 0)],
)
self.assertSequenceEqual(
Article.objects.datetimes('pub_date', 'week'),
["datetime.datetime(2005, 7, 25, 0, 0)"])
self.assertQuerysetEqual(
Article.objects.datetimes('pub_date', 'day'),
["datetime.datetime(2005, 7, 28, 0, 0)",
"datetime.datetime(2005, 7, 29, 0, 0)",
"datetime.datetime(2005, 7, 30, 0, 0)",
"datetime.datetime(2005, 7, 31, 0, 0)"])
self.assertQuerysetEqual(
[datetime.datetime(2005, 7, 25, 0, 0)],
)
self.assertSequenceEqual(Article.objects.datetimes('pub_date', 'day'), [
datetime.datetime(2005, 7, 28, 0, 0),
datetime.datetime(2005, 7, 29, 0, 0),
datetime.datetime(2005, 7, 30, 0, 0),
datetime.datetime(2005, 7, 31, 0, 0),
])
self.assertSequenceEqual(
Article.objects.datetimes('pub_date', 'day', order='ASC'),
["datetime.datetime(2005, 7, 28, 0, 0)",
"datetime.datetime(2005, 7, 29, 0, 0)",
"datetime.datetime(2005, 7, 30, 0, 0)",
"datetime.datetime(2005, 7, 31, 0, 0)"])
self.assertQuerysetEqual(
[
datetime.datetime(2005, 7, 28, 0, 0),
datetime.datetime(2005, 7, 29, 0, 0),
datetime.datetime(2005, 7, 30, 0, 0),
datetime.datetime(2005, 7, 31, 0, 0),
],
)
self.assertSequenceEqual(
Article.objects.datetimes('pub_date', 'day', order='DESC'),
["datetime.datetime(2005, 7, 31, 0, 0)",
"datetime.datetime(2005, 7, 30, 0, 0)",
"datetime.datetime(2005, 7, 29, 0, 0)",
"datetime.datetime(2005, 7, 28, 0, 0)"])
[
datetime.datetime(2005, 7, 31, 0, 0),
datetime.datetime(2005, 7, 30, 0, 0),
datetime.datetime(2005, 7, 29, 0, 0),
datetime.datetime(2005, 7, 28, 0, 0),
],
)

def test_datetimes_has_lazy_iterator(self):
pub_dates = [
Expand Down
Loading