Analyst 18   Software Engineering and other random() subjects

Ready to use Structure for Django Tests + Examples (Pt. 2)

This article builds up on the Structure for Django Tests and shows how you can easily display individual running times for each test in a group or suite - and you can use in Flask tests too! - check out my Flask App to generate summaries.

The Problem

By default, Django will only display the execution time for the entire series of tests ran, for example:

$ python manage.py test tests.functional.test_search 

Creating test database for alias default...
....
----------------------------------------------------------------------
Ran 4 tests in 34.365s

OK

That’s not a lot of information here… What if one of these 29 tests was misbehaving and taking too long? Wouldn’t it be better if we could

  • Tell the sequence in which tests are being ran?
  • Know the time each one of these tests took to execute?```

Something like this would be nice:

$ python manage.py test tests.functional.test_search 

Creating test database for alias 'default'...

    testing function 'setUp'
    [OK] in 'setUp' 9.12 sec

    testing function 'test_ajax_search_thing'
    [OK] in 'test_ajax_search_thing' 3.94 sec
.
    testing function 'setUp'
    [OK] in 'setUp' 10.81 sec

    testing function 'test_ajax_search_model'
    [OK] in 'test_ajax_search_model' 9.55 sec
.
----------------------------------------------------------------------
Ran 2 tests in 34.270s

OK

Now it’s much easier to understand what’s going on: setUp() - which runs before every test as expected, is taking between 9-10s!

Time to change dev notebook!

Tracking Time with a Python Decorator

Sometimes you find solutions in places you were not looking for, and in this case I was researching scraping projects when I came upon Lucas Ou-Young’s excellent resource on Article scraping & curation, where he uses a decorator to produce the detailed output above.

Let’s modify the code on the previous post to do that:

Open tests/testing_utilities and add the decorator code:


# add this to existing code:
def print_test_time_elapsed(method):
    """
    Utility method for print verbalizing test suite, prints out
    time taken for test and functions name, and status
    """
    def run(*args, **kw):
        ts = time.time()
        print('\n\ttesting function %r' % method.__name__)
        method(*args, **kw)
        te = time.time()
        print('\t[OK] in %r %2.2f sec' % (method.__name__, te - ts))

    return run

That is so slick!

Now all we have to do is import print_test_time_elapsed into our test suites and use it as a decorator:

For example : tests/unit/test_requests.py

from django.test import TestCase
from django.test.client import RequestFactory
from my_application.views import home, ajax_search
from ..testing_utilities import populate_test_db, print_test_time_elapsed

class RequestTests(TestCase):

    @print_test_time_elapsed
    def setUp(self):
        # Every test needs access to the request factory.
        self.factory = RequestFactory()
        # Add records to test DB
        populate_test_db()

    @print_test_time_elapsed
    def test_home_view_without_client(self):
        request = self.factory.get('/')
        response = home(request)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Some text that should be in the HOME view")

    @print_test_time_elapsed
    def test_category_view(self):
        request = self.factory.get(reverse('category',
                                           kwargs={'cat_id': 1}))
        response = category(request, 1)
        self.assertEqual(response.status_code, 200)


    # .......

References

Django’s Official Tutorial and Django’s Testing Tools Docs - Comprehensive resources, but they made more sense to me after I understood the different test types.

The Most Efficient Django Test - If you could only write one single test for your Django App, this would be it.

Marina Mele’s Django Tutorial - Not just a great Django tutorial, but also a good introduction on using Selenium Webdriver and LiveServerTestCase for functional tests.

Toast Drive’s Guide to Testing in Django #2 - The reference used to testing POST requests.

Newspaper3k: Article scraping & curation - Great reference on scraping that also has an interesting approach on how to measure individual test’s running times.