web-dev-qa-db-ja.com

Django:単体テストからSQLクエリをカウントする方法はありますか?

ユーティリティ関数によって実行されたクエリの数を調べようとしています。この関数の単体テストを作成しましたが、関数は正常に機能しています。私がやりたいのは、関数によって実行されたSQLクエリの数を追跡して、リファクタリング後に改善があったかどうかを確認できるようにすることです。

def do_something_in_the_database():
    # Does something in the database
    # return result

class DoSomethingTests(Django.test.TestCase):
    def test_function_returns_correct_values(self):
        self.assertEqual(n, <number of SQL queries executed>)

編集:これには保留中のDjango 機能リクエスト があることがわかりました。しかし、チケットはまだ開いています。それまでの間、これを回避する別の方法があります?

46
Manoj Govindan

Django 1.3なので、この目的のために正確に利用できる assertNumQueries があります。

57
Mitar

Vinayの応答は正しいですが、マイナーな追加が1つあります。

Djangoの単体テストフレームワークは、実行時にDEBUGを実際にFalseに設定するため、settings.pyに何があっても、デバッグモードを再度有効にしない限り、単体テストのconnection.queriesには何も入力されません。 Djangoドキュメントは この理由 を次のように説明しています:

構成ファイルのDEBUG設定の値に関係なく、すべてのDjangoテストはDEBUG = Falseで実行されます。これは、コードの観察された出力が本番環境で見られるものと一致することを確認するためです。設定。

デバッグを有効にしてもテストに影響がないことが確実な場合(DBヒットを具体的にテストしている場合など)、解決策は、単体テストでデバッグを一時的に再度有効にしてから設定することです。後で戻る:

def test_myself(self):
    from Django.conf import settings
    from Django.db import connection

    settings.DEBUG = True
    connection.queries = []

    # Test code as normal
    self.assert_(connection.queries)

    settings.DEBUG = False
41
Jarret Hardie

pytestを使用している場合、pytest-Django has Django_assert_num_queries この目的のためのフィクスチャ:

def test_queries(Django_assert_num_queries):
    with Django_assert_num_queries(3):
        Item.objects.create('foo')
        Item.objects.create('bar')
        Item.objects.create('baz')
7
tvorog

settings.pyDEBUGをTrueに設定している場合(おそらくテスト環境ではそうです)、テストで実行されたクエリを次のようにカウントできます。

from Django.db import connection

class DoSomethingTests(Django.test.TestCase):
    def test_something_or_other(self):
        num_queries_old = len(connection.queries)
        do_something_in_the_database()
        num_queries_new = len(connection.queries)
        self.assertEqual(n, num_queries_new - num_queries_old)
4
Vinay Sajip

最新のDjango(> = 1.8)では十分に文書化されています(1.7でも文書化されています) ここ 、メソッドreset_queriesがありますconnection.queries = []を割り当てる代わりに、実際にエラーが発生します。Django> = 1.8で機能するようなものです。

class QueriesTests(Django.test.TestCase):
    def test_queries(self):
        from Django.conf import settings
        from Django.db import connection, reset_queries

        try:
            settings.DEBUG = True
            # [... your ORM code ...]
            self.assertEquals(len(connection.queries), num_of_expected_queries)
        finally:
            settings.DEBUG = False
            reset_queries()

また、setUp/tearDownでクエリをリセットして、finally句で行うのではなく、テストごとにクエリがリセットされるようにすることも検討できますが、この方法はより明示的です(ただし、より冗長です)。または、を使用できます。 )reset_queriesは、0から数えてクエリを評価するために必要な回数だけtry句に入力します。

3
danius

TestCase( assertNumQueries を使用)を使用したくない場合、または設定をDEBUG = Trueに変更したくない場合は、コンテキストマネージャーCaptureQueriesContext( assertNumQueries usingと同じ)を使用できます。

from Django.db import ConnectionHandler
from Django.test.utils import CaptureQueriesContext

DB_NAME = "default"  # name of db configured in settings you want to use - "default" is standard
connection = ConnectionHandler()[DB_NAME]
with CaptureQueriesContext(connection) as context:
    ... # do your thing
num_queries = context.initial_queries - context.final_queries
assert num_queries == expected_num_queries

db設定

2
Daniel Barton