views:

387

answers:

2

I am trying to find out the number of queries executed by a utility function. I have written a unit test for this function and the function is working well. What I would like to do is track the number of SQL queries executed by the function so that I can see if there is any improvement after some refactoring.

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>)

EDIT: I found out that there is a pending Django feature request for this. However the ticket is still open. In the meantime is there another way to go about this?

+2  A: 

If you have DEBUG set to True in your settings.py (presumably so in your test environment) then you can count queries executed in your test as follows:

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)
Vinay Sajip
Thanks. I tried it out but strangely enough len(connection.queries) is turning out to be Zero(!) *before and after* the call to the function. I tested it after replacing the function call with a straightforward call to MyModel.objects.filter() and still no luck. FYI I am using Django 1.1.
Manoj Govindan
Update: the mechanism works if I execute the function interactively using iPython. Of course this is against the development database as opposed to the transient test database. Does the discrepancy have something to do with the way Django executes tests in a transaction?
Manoj Govindan
+6  A: 

Vinay's response is correct, with one minor addition.

Django's unit test framework actually sets DEBUG to False when it runs, so no matter what you have in settings.py, you will not have anything populated in connection.queries in your unit test unless you re-enable debug mode. The Django docs explain the rationale for this as:

Regardless of the value of the DEBUG setting in your configuration file, all Django tests run with DEBUG=False. This is to ensure that the observed output of your code matches what will be seen in a production setting.

If you're certain that enabling debug will not affect your tests (such as if you're specifically testing DB hits, as it sounds like you are), the solution is to temporarily re-enable debug in your unit test, then set it back afterward:

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
Jarret Hardie
Thanks. Toggling DEBUG is precisely what I needed to do. :)
Manoj Govindan
An additional note: you should really wrap your test code in a try: block, putting settings.DEBUG = False in a corresponding finally: block. This way your other tests won't get "tainted" by the DEBUG setting if this one fails.
SmileyChris