Skip to main content

A Django test utility to assert the number of queries per model.

Project description

Django Assert Model Queries

This project seeks to assist asserting the number of queries per model during testing.

Note: This does so by monkey-patching the SQLCompiler classes. It's not something that should be relied upon in production.

Installation

pip install django-assert-model-queries

Usage

There are integrations for both pytest and Django / unittest. Both of which use the context manager, django_assert_model_queries.AssertModelQueries under the hood.

The basic usage is to define a dictionary of expected queries to be evaluated at the end of the context manager's scope. If the counts differ, a helpful error message will be rendered indicating what the differences were and what all the queries were during the context.

from django_assert_model_queries import AssertModelQueries
from testapp.models import Community

with AssertModelQueries({"testapp.Community": 2}):
    Community.objects.create(name="test")
    Community.objects.update(name="test")

When an unexpected query runs, this AssertModelQueries will tell you which model generated an unexpected query.

Example error

Here is an example of what you can expect from the tool:

>>>  from django_assert_model_queries import AssertModelQueries
>>>  from django.contrib.auth.models import User
>>>  with AssertModelQueries({}):
>>>      User.objects.first()

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[1], line 3
      1 from django_assert_model_queries import AssertModelQueries
      2 from django.contrib.auth.models import User
----> 3 with AssertModelQueries({}):
      4     User.objects.only("id").first()

File ~/site-packages/django_assert_model_queries/test.py:145, in AssertModelQueries.__exit__(self, exc_type, exc_value, traceback)
    142 if exc_type is not None:
    143     return
--> 145 self.handle_assertion(actual, expected)
    146 self.expected_model_counts = None

File ~/site-packages/django_assert_model_queries/test.py:172, in AssertModelQueries.handle_assertion(self, actual, expected)
    170         pytest.fail(self.failure_message(actual, expected))
    171 else:
--> 172     assert actual == expected, self.failure_message(actual, expected)

AssertionError: {'auth.User': 1} != {}
- {'auth.User': 1}
+ {}

All queries:
SELECT "auth_user"."id" FROM "auth_user" ORDER BY "auth_user"."id" ASC LIMIT 1

Integrating with pytest

If you use pytest, you can use the fixture assert_model_queries as a short-cut.

# pytest example

import pytest
from testapp.models import Community


class TestPytestIntegration:
    @pytest.mark.django_db
    def test_assert_model_queries(self, assert_model_queries):
        with assert_model_queries({"testapp.Community": 1}):
            Community.objects.create(name="test")

        with assert_model_queries({
          "testapp.Community": 2,
          "testapp.Chapter": 1,
          "testapp.Community_topics": 1,
        }):
            Community.objects.all().delete()

Integrating with unittest

If you test with Django's TestCase, inherit from the mixin ModelNumQueriesHelper to be able to utilize the self.assertModelQueries assertion method.

# Django TestCase example

from django.test import TestCase
from django_assert_model_queries import AssertModelQueries, ModelNumQueriesHelper
from testapp.models import Community

class TestDjangoIntegration(ModelNumQueriesHelper, TestCase):
    def test_assert_model_num_queries_context(self):
        with AssertModelQueries({"testapp.Community": 1}):
            Community.objects.create(name="test")
        with AssertModelQueries({"testapp.Community": 2, "testapp.Chapter": 1, "testapp.Community_topics": 1}):
            Community.objects.all().delete()

class TestDjangoHelperIntegration(ModelNumQueriesHelper, TestCase):
    def test_helper(self):
        with self.assertModelQueries({"testapp.Community": 1}):
            Community.objects.create(name="test")
        with self.assertModelQueries({"testapp.Community": 2, "testapp.Chapter": 1, "testapp.Community_topics": 1}):
            Community.objects.all().delete()

Complex usages

There are a few parameters that may help in certain scenarios.

  • ignore: A collection of models that should be ignored. For example, maybe you want to ignore Session queries if you're using a database backed session.
  • strict=False: You can limit the count evaluation to just the models specified in the expect_model_counts collection. Be warned, this can hide N+1 issues.

To use these, you must specify them when instantiating AssertModelQueries.

from django_assert_model_queries import AssertModelQueries
from django.contrib.sessions.models import Session

assert_context = AssertModelQueries(ignore={Session})
with assert_context({"testapp.Community": 1}):
    do_something()


assert_context = AssertModelQueries(strict=False)
with assert_context({"testapp.Community": 1}):
    do_something()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_assert_model_queries-1.1.0.tar.gz (21.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_assert_model_queries-1.1.0-py3-none-any.whl (8.0 kB view details)

Uploaded Python 3

File details

Details for the file django_assert_model_queries-1.1.0.tar.gz.

File metadata

File hashes

Hashes for django_assert_model_queries-1.1.0.tar.gz
Algorithm Hash digest
SHA256 e9d77f03fcc6adc12175bd2b9b59bd43f2990547f4e68e53b3a647a1b5e9d935
MD5 b473c37598afa4b3f693ebb2a32cab15
BLAKE2b-256 ad97d493a1899a5b6087f4e8c97cbc90dcb379fa6c85ea0768da5bbf9862538e

See more details on using hashes here.

File details

Details for the file django_assert_model_queries-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_assert_model_queries-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e09cd62780b0e808fa933200fb7cc699b9d5061cd16dba6ce666559243b7581b
MD5 1b0d798b4eca66d9bc88e8e70347aa2a
BLAKE2b-256 626b7b24b27182aff46a36e3469388fee09fd664b909358e2736e86b82160638

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page