Background
In a previous post I wrote about comparing Django’s Q object instances. The original code was Python 2 with unittest and was due for an update.
The previous issue with comparing Django’s Q objects remains the same:
Django’s Q object does not implement __cmp__ and neither does Node which it extends (Node is in the django.utils.tree module).
Unfortunately, that means that comparison of Q objects that are equal fails.
A simple Python 3 solution
The following is a Python 3.6 assertion helper for use with pytest that uses the original strategy of comparing the string versions of the Q objects.
from django.db.models import Q
def assert_q_equal(left, right):
"""
Test two Q objects for equality. Does is not match commutative.
Args:
left (Q)
right (Q)
Raises:
AssertionError: When -
* `left` or `right` are not an instance of `Q`
* `left` and `right` are not considered equal.
"""
assert isinstance(left, Q), f'{left.__class__} is not subclass of Q'
assert isinstance(right, Q), f'{right.__class__} is not subclass of Q'
assert str(left) == str(right), f'Q{left} != Q{right}'
This time the helper is just a function rather than a mixin for unittest.TestCase.
isinstance is used for comparison so that any instance of a class derived from Q can also be matched. The assertions have secondary expressions in the form of f-strings to give helpful output without raising a custom assertion.
When two Q instances do not match, pytest shows the following output:
______________________ test_neq_multi_not_commutative ______________________ test_assert_q_equal.py:83: in test_neq_multi_not_commutative assert_q_equal(q_a, q_b) test_assert_q_equal.py:22: in assert_q_equal assert str(left) == str(right), f'Q{left} != Q{right}' E AssertionError: Q(AND: ('speed', 12), ('direction', 'north')) != Q(AND: ('direction', 'north'), ('speed', 12)) E assert "(AND: ('spee...n', 'north'))" == "(AND: ('direc...'speed', 12))" E - (AND: ('speed', 12), ('direction', 'north')) E + (AND: ('direction', 'north'), ('speed', 12)) ==================== 1 failed, 7 passed in 0.07 seconds ====================
The important thing is to adjust your assertion helpers to best fit the needs of your test suite and team.