How do I test Django QuerySets are equal?
Solution 1
Use assertQuerysetEqual, which is built to compare the two querysets for you. You will need to subclass Django's django.test.TestCase
for it to be available in your tests.
Solution 2
By default assertQuerysetEqual
uses repr()
on the first argument. This is why you were having issues with the strings in the queryset comparison.
To work around this you can override the transform
argument with a lambda
function that doesn't use repr()
:
self.assertQuerysetEqual(queryset_1, queryset_2, transform=lambda x: x)
Solution 3
I just had the same problem. The second argument of assertQuerysetEqual
needs to be a list of the expected repr()s as strings. Here is an example from the Django test suite:
self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"], ordered=False)
Solution 4
An alternative, but not necessarily better, method might look like this (testing context in a view, for example) when using pytest:
all_the_things = Things.objects.all()
assert set(response.context_data['all_the_things']) == set(all_the_things)
This converts it to a set, which is directly comparable with another set. Be careful with the behaviour of set
though, it might not be exactly what you want since it will remove duplicates and ignore the order of objects.
Solution 5
I ended up solving this issue using map
to repr()
each entry in the queryset inside the self.assertQuerysetEqual
call, e.g.
self.assertQuerysetEqual(queryset_1, map(repr, queryset_2))
jz999
Updated on January 20, 2020Comments
-
jz999 over 4 years
I am trying to test my Django views. This view passes a QuerySet to the template:
def merchant_home(request, slug): merchant = Merchant.objects.get(slug=slug) product_list = merchant.products.all() return render_to_response('merchant_home.html', {'merchant': merchant, 'product_list': product_list}, context_instance=RequestContext(request))
and test:
def test(self): "Merchant home view should send merchant and merchant products to the template" merchant = Merchant.objects.create(name='test merchant') product = Product.objects.create(name='test product', price=100.00) merchant.products.add(product) test_client = Client() response = test_client.get('/' + merchant.slug) # self.assertListEqual(response.context['product_list'], merchant.products.all()) self.assertQuerysetEqual(response.context['product_list'], merchant.products.all())
EDIT I am using
self.assertQuerysetEqua
l instead ofself.assertListEqual
. Unfortunately this still doesn't work, and the terminal displays this:['<Product: Product object>'] != [<Product: Product object>]
assertListEqual
raises:'QuerySet' object has no attribute 'difference'
andassertEqual
does not work either, althoughself.assertSetEqual(response.context['product_list'][0], merchant.products.all()[0])
does pass.I assume this is because the QuerySets are different objects even though they contain the same model instances.
How do I test that two QuerySets contain the same data? I am even testing this correctly? This is my 4th day learning Django so I would like to know best practices, if possible. Thanks.
-
jz999 almost 11 yearsAwesome, this is helpful, though now my tests fails because of this: AssertionError: ['<Product: Product object>'] != [<Product: Product object>] The only difference I see is that there are quotes around the first product object. They are both of class QuerySet
-
Griffith Rees over 9 yearsSee stackoverflow.com/a/14189017/678486 (full disclosure this is my answer).
-
tread about 6 years
AssertionError: <QuerySet [<Account: Test Account>]> != <map object at 0x10c7f7ac8>
-
Daniel Holmes over 5 yearsI found this also worked for comparing two dictionaries with the same keys and the same queryset values:
self.assertQuerysetEqual(dict_1, dict_2, transform=lambda x: x)
-
rgov about 5 yearsYou probably do not need to use
set(list())
,set()
would do. -
ggorlen over 3 yearsThis doesn't add anything substantial to the existing answers. I recommend upvoting those answers instead.