代码之家  ›  专栏  ›  技术社区  ›  Manoj Govindan

Django:有没有一种方法来计算单元测试中的SQL查询?

  •  42
  • Manoj Govindan  · 技术社区  · 15 年前

    我试图找出实用程序函数执行的查询数。我为这个函数编写了一个单元测试,这个函数运行良好。我想做的是跟踪函数执行的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 feature request 为此。但是票还是开着的。与此同时,还有别的办法吗?

    6 回复  |  直到 7 年前
        1
  •  51
  •   user    9 年前

    自Django 1.3以来, assertNumQueries 完全适用于此目的。

        2
  •  39
  •   Jarret Hardie    15 年前

    维尼的回答是正确的,只加了一点。

    Django的单元测试框架实际上在运行时将debug设置为false,因此无论您使用的是什么 settings.py ,您将没有任何填充内容 connection.queries 在单元测试中,除非重新启用调试模式。Django医生解释 rationale for this AS:

    无论配置文件中调试设置的值如何,所有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
    
        3
  •  4
  •   Vinay Sajip    15 年前

    如果你有 DEBUG 在您的 settings.py (在您的测试环境中可能是这样)然后您可以按如下方式对测试中执行的查询进行计数:

    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
  •  3
  •   danius    9 年前

    在现代Django(>=1.8)中,它有很好的文档记录(也有1.7的文档记录) here 你有办法 重新设置查询 而不是分配 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()
    

    您还可以考虑在安装/拆卸时重置查询,以确保为每个测试重置查询,而不是在finally子句上执行查询,但这种方法更显式(尽管更详细),或者您可以使用 重新设置查询 在try子句中,计算从0开始的查询所需的次数。

        5
  •  3
  •   tvorog    7 年前

    如果您正在使用 pytest , pytest-django 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')
    
        6
  •  2
  •   Daniel Barton    8 年前

    如果不想使用testcase(与 assertNumQueries )或者将设置更改为debug=true,您可以使用上下文管理器CaptureQueriesContext(与 断言数查询 使用)。

    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 settings