Skip to main content

django-sql-test

Prevent SQL regressions, detect N+1 queries, and visualize query diffs in your Django tests. A test mixin that captures, snapshots, and diffs all executed SQL queries in your test output.

Package version Supported Python versions License
$ poetry run python manage.py test path.to.test.FooTest.test_bar
======================================================================
FAIL: test_bar (path.to.test.FooTest.test_bar)
 ...
AssertionError: 5 != 2 : 5 queries executed, 2 expected
Queries diff:
- SELECT polls_choice.id FROM polls_choice
+ SELECT polls_choice.id FROM polls_choice WHERE polls_choice.votes >= N
+ SELECT polls_question.id FROM polls_question WHERE polls_question.id = N LIMIT N
+ SELECT polls_question.id FROM polls_question WHERE polls_question.id = N LIMIT N
+ SELECT polls_question.id FROM polls_question WHERE polls_question.id = N LIMIT N
  SELECT COUNT(*) AS __count FROM polls_question

Table of Contents

Requirements

  • Django >= 4.0
  • Python >= 3.9

Installation

pip install django-sql-test

Quickstart

In your test.py just import NumNewQueriesMixin and add it as a parent:

from django.test import TestCase
from django_sql_test import NumNewQueriesMixin

class FooTest(NumNewQueriesMixin, TestCase):
    def test_bar(self):
        with self.assertNumNewQueries(2):
            response = self.client.get(self.url)

        self.assertEqual(response.status_code, 200)

What you got before adding NumNewQueriesMixin:

$ poetry run python manage.py test path.to.test.FooTest.test_bar
======================================================================
FAIL: test_bar (path.to.test.FooTest.test_bar)
  ...
AssertionError: 5 != 2 : 5 queries executed, 2 expected
Captured queries were:
1. SELECT "polls_choice"."id" FROM "polls_choice" WHERE "polls_choice"."votes" >= 0
2. SELECT "polls_question"."id" FROM "polls_question" WHERE "polls_question"."id" = 1 LIMIT 21
3. SELECT "polls_question"."id" FROM "polls_question" WHERE "polls_question"."id" = 1 LIMIT 21
4. SELECT "polls_question"."id" FROM "polls_question" WHERE "polls_question"."id" = 1 LIMIT 21
5. SELECT COUNT(*) AS "__count" FROM "polls_question"

What you get after adding NumNewQueriesMixin:

$ poetry run python manage.py test path.to.test.FooTest.test_bar
======================================================================
FAIL: test_bar (path.to.test.FooTest.test_bar)
 ...
AssertionError: 5 != 2 : 5 queries executed, 2 expected
Queries diff:
- SELECT polls_choice.id FROM polls_choice
+ SELECT polls_choice.id FROM polls_choice WHERE polls_choice.votes >= N
+ SELECT polls_question.id FROM polls_question WHERE polls_question.id = N LIMIT N
+ SELECT polls_question.id FROM polls_question WHERE polls_question.id = N LIMIT N
+ SELECT polls_question.id FROM polls_question WHERE polls_question.id = N LIMIT N
  SELECT COUNT(*) AS __count FROM polls_question

Configuration

Configure via your Django settings:

Setting Default Description
SQL_TEST_GENERALIZED_DIFF True Hides SQL query parameters by replacing them with placeholders.
SQL_TEST_DIFF_ONLY False If set to True, shows only SQL queries that were added or removed; otherwise, shows all queries.
SQL_TEST_DIFF_NEW_COLOR "\033[1;32m" Color code for newly added SQL queries (green).
SQL_TEST_DIFF_OLD_COLOR "\033[1;31m" Color code for removed SQL queries (red).
SQL_TEST_DIFF_DEFAULT_COLOR "\033[0m" Base console color code for unchanged SQL queries.
SQL_TEST_ENGINE "file" Engine used to store the last successful SQL queries. Default "file" engine stores data in the file from SQL_TEST_ENGINE_SETTINGS["filename"], or in .django_sql_test_queries at the project root if not set. You can implement a custom engine by inheriting from django_sql_test.engine.Engine, overriding its methods, and specifying its full path, e.g., SQL_TEST_ENGINE = "path.to.YourEngine".
SQL_TEST_ENGINE_SETTINGS {} Dictionary of settings passed to the engine's constructor. For the default "file" engine, you can pass the path to the file where queries will be stored in JSON format.

SQL_TEST_GENERALIZED_DIFF

If set to True:

Queries diff:
+ SELECT polls_choice.id FROM polls_choice WHERE polls_choice.votes >= N

If set to False:

Queries diff:
+ SELECT "polls_choice"."id" FROM "polls_choice" WHERE "polls_choice"."votes" >= 0

API Reference

NumNewQueriesMixin

Method Description
assertNumNewQueries Works just like django.test.testcases.TransactionTestCase.assertNumQueries, but also prints the diff of queries compared to the last successful run, if any.
assertNumQueries Acts like assertNumNewQueries. It can be used if you don’t want to replace every occurrence of assertNumQueries with assertNumNewQueries in your tests. Simply inherit from it in your test class, for example: class PaginatorsTestCase(NumNewQueriesMixin, ViewTestCase), and everything will work out of the box.

Contributing

We welcome contributions! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/YourFeature)
  3. Write tests and ensure coverage
  4. Run and pass:
    poetry run isort -c --diff --settings-file pyproject.toml .
    poetry run black --diff --config pyproject.toml --check .
    poetry run python runtests.py
    
  5. Submit a pull request

License

This project is licensed under the MIT License. See LICENSE for details.

Changelog

All notable changes are documented on the Releases page.

Metadata

Release files for django-sql-test 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-sql-test 0.3.0
File Size Uploaded
django_sql_test-0.3.0.tar.gz 5.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-sql-test 0.3.0
File Interpreter ABI Platform
django_sql_test-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 12.9 kB

Release files / django_sql_test-0.3.0.tar.gz

Download URL django_sql_test-0.3.0.tar.gz
Size 5.8 kB
Tags Source
SHA-256 checksum
How to use checksums
28d6dd56870b043ffc57da8cbd6a08a551e09203c5f47d66c6ac1950b7d36c86
BLAKE2b-256 checksum
How to use checksums
0145a68301650a159a3c7d3d503bd687e2697b78c8dc0ec46b2ea1c4892685f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.1.1 CPython/3.13.2 Darwin/24.5.0

Release files / django_sql_test-0.3.0-py3-none-any.whl

Download URL django_sql_test-0.3.0-py3-none-any.whl
Size 7.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1051eea2bc2277a84f9076343b986cf4b22451d4c4e6b2bd8e5e4c2ecd98cbd3
BLAKE2b-256 checksum
How to use checksums
33fe8b722e9690d6d0a31919e9e414ab39ab89f16a59a44b2af45cb094d1daf8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.1.1 CPython/3.13.2 Darwin/24.5.0

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page