Django:有没有一种方法来计算unit testing的SQL查询?

我正在试图找出一个实用函数执行的查询的数量。 我已经为这个函数写了一个unit testing,这个函数运行的很好。 我想要做的是跟踪由该函数执行的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 function请求 。 不过票还是开着的。 同时还有另外一种方法可以解决这个问题吗?

由于Django 1.3有一个assertNumQueries可用于此目的。

Vinay的回答是正确的,只有一个小小的补充。

在运行时,Django的unit testing框架实际上将DEBUG设置为False,因此无论您在settings.py拥有什么内容,都不会在unit testing中的connection.queries中填充任何内容,除非重新启用debugging模式。 Django文档解释了这个理由 :

无论configuration文件中的DEBUG设置的值如何,所有的Djangotesting都以DEBUG = False运行。 这是为了确保观察到的代码输出与在生产设置中看到的内容相匹配。

如果您确定启用debugging不会影响您的testing(例如,如果您是专门testing数据库命中,听起来就像是你),解决scheme是暂时重新启用unit testing中的debugging,然后设置它之后回来:

 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 

如果你的settings.py DEBUG设置为True(大概是在你的testing环境中),那么你可以计算在你的testing中执行的查询,如下所示:

 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) 

在现代的Django(> = 1.8)中,它有很好的文档logging(它也logging为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上重新设置查询,以确保每个testing的查询都被重置,而不是在finally子句上进行,但这种方式更加明确(尽pipe更详细),或者可以在try子句中多次使用reset_queries因为您需要评估从0开始计数的查询。

如果您不想使用TestCase(使用assertNumQueries )或将设置更改为DEBUG = True,则可以使用上下文pipe理器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 

数据库设置

如果你正在使用pytestpytest-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')