Django Custom Test Runner

  

In a nutshell, a Middleware is a regular Python class that hooks into Django’s request/response life cycle. Thoseclasses holds pieces of code that are processed upon every request/response your Django application handles.

The Middleware classes doesn’t have to subclass anything and it can live anywhere in your Python path. The only thingDjango cares about is the path you register in the project settings MIDDLEWARE_CLASSES.

Running the Test Suite. The easiest way to run the django-filter tests is to check out the source code and create a virtualenv where you can install the test dependencies. Django-filter uses a custom test runner to configure the environment, so a wrapper script is available to set up and run the test suite. Introduction to Python/Django testing: Basic Unit Tests¶. Last post we talked about how to set up and use doc tests inside of Django. Today, in the second post of the series, we’ll be talking about how to use the other testing framework that comes with Python, unittest. Unittest is a xUnit type of testing system (JUnit from the Java world is another example) implemented in Python.

Your Middleware class should define at least one of the following methods:

  • Called during request:
    • process_request(request)
    • process_view(request, view_func, view_args, view_kwargs)
  • Called during response:
    • process_exception(request, exception) (only if the view raised an exception)
    • process_template_response(request, response) (only for template responses)
    • process_response(request, response)

How it works?

The Middlware classes are called twice during the request/response life cycle. For that reason, the order you definethe Middlwares in the MIDDLEWARE_CLASSES configuration is important.

Let’s have a look on the built-in Middleware classes the django-admin startproject command sets up:

During the request cycle, the Middleware classes are executed top-down, meaning it will first executeSecurityMiddleware, then SessionMiddleware all the way until XFrameOptionsMiddleware. For each of the Middlewaresit will execute the process_request() and process_view() methods.

At this point, Django will do all the work on your view function. After the work is done (e.g. querying the database,paginating results, processing information, etc), it will return a response for the client.

During the response cycle, the Middleware classes are executed bottom-up, meaning it will first executeXFrameOptionsMiddleware, then MessageMiddleware all the way until SecurityMiddleware. For each of the Middlewaresit will execute the process_exception(), process_template_response() and process_response() methods.

Finally Django will deliver the response for the client. It is important to note that process_exception() is onlyexecuted if a exception occurs inside the view function and process_template_response() is only executed if thereis a template in the response.

The image below was extracted from the official Django documentation and it represents well the process describedabove.

Creating a Custom Middleware

To ilustrated this post, let’s create a custom Middleware class that intercept all the exceptions that occur in ourview functions then grab the exception message and query the StackOverflow API and return the three top answers andprint it to the terminal.

I will call it StackOverflow Exception Troubleshooting, or simply SOET. So it’s gonna be a Middleware fordebugging, meant to run only when DEBUG=True.

This is what our Middleware will look like:

Our view function thorws an uncaugh exception, the SOET Middleware process it, search for a solution onStackOverflow, and print the three most relevant results for the developer directly in this terminal window.

Cool right? You will see how easy it is to implement it.

Custom

Getting Started

For this example I created a Django app named soet because I wanted to make it available for everyone. I will getback to that later. But if you already have an app that makes sense to create the middleware in, go ahead.

Inside the soet app I created a file named middleware.py. At the moment it looks like that:

Now I gotta register my new Middleware in the MIDDLEWARE_CLASSES configuration:

Django Custom Test Runner

I’ve registered it as the last one because the process_exception() method is only processed during the responsecycle, and I want my middleware to be executed first so no other Middleware supress the thrown exception.

At this point our brand new Middleware isn’t doing anything really. But it is already being executed. We can test itby putting a print statement inside the process_exception() method. Also it is important that our method alwaysreturn None so to keep the flow of the Middlewares processing. We don’t want to affect the behavior of what isreturned to the client.

Now let’s make sure we are only executing this Middleware if the DEBUG=True:

Consuming the StackOverflow API

The idea now is to use the exception message and the exception name to query the StackOverflow database through its APIin order to find relevant solutions.

To save us some time and avoid struggling with the python standard libraries urllib and urllib2, let’s just use theawesome Requests library.

Also I will not go into much detail about the StackOverflow API. The previous link will take youto the official documentation where you can learn more. That being said, I will stick with the /2.2/search endpoint.

So at this point we are already consuming the StackOverflow API! It is pretty much it. Now we are listing the top 3questions with the tags python and django, displaying its title and the question url.

Now it is a matter of selecting the right fields and displaying it in the terminal, using the same strategy.

Try it Live

The source code of this little experiment is available on GitHub. The package is alsoavailable on PyPI.

Here is a quick start if you want to try it:

Django unit testDjango Custom Test Runner

1. Install using pip:

2. Include “soet” to your INSTALLED_APPS:

Django Run

3. Include “StackOverflowMiddleware” to your MIDDLEWARE_CLASSES:

Django

4. Make sure you are running your project with DEBUG=True.

Django Test Db

5. Start your development server and wait for the view exceptions (or not).