Django Custom Test Runner

TESTRUNNER = 'rainbowtests.test.runner.RainbowDiscoverRunner' Django test runner to the RainbowTestSuiteRunner. This was removed in django 1.8, so using this test runner on newer projects will fail: TESTRUNNER = 'rainbowtests.test.simple.RainbowTestSuiteRunner' Then run your tests! Python/Django Compatibility. For Django versions 1.1 and later, PyCharm supports custom test runner, if this test runner is a class. By default, all tests are executed one by one. You can change this behavior for the pytest testing framework and execute your tests in parallel. Run tests in parallel. Enable test multiprocessing to optimize execution of your pytest tests. If this checkbox is selected, Django test will run with the specified custom settings, rather than with the default ones. Specify the fully qualified name of the file that contains Django settings. You can either type it manually, in the text field to the right, or click the browse button, and select one in the dialog that opens. Essentially, Django’s core test runner class, DiscoverRunner, can now optionally use Python’s logging framework. This is useful for customizing its output, or for testing custom runner subclasses and making assertions on its output. Thanks to Chris Jerdonek and Daniyal Abbasi for the contributions in Ticket #32552. Thanks to Ahmad Hussein.

Django custom test runner free

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 best base class for most tests is django.test.TestCase. This test class creates a clean database before its tests are run, and runs every test function in its own transaction. The class also owns a test Client that you can use to simulate a user interacting with the code at the view level.

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.

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?

Test

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.

Django Custom Test Runner Download

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.

Django Test Teardown

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.

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:

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.

Django Transactiontestcase

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

Django transactiontestcase

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:

1. Install using pip:

2. Include “soet” to your INSTALLED_APPS:

Django Custom Test Runner

3. Include “StackOverflowMiddleware” to your MIDDLEWARE_CLASSES:

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

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

Editor's note: This post was originally published in February 2016 and was updated in August 2017 to incorporate improvements suggested by our readers. It has also been tested for compatibility as of the Django 1.11 release.

Testing in a Django project ensures the latest version of a project is as bug-free as possible. But when deploying, you’re dealing with multiple versions of the project through the migrations.

The test runner is extremely helpful in its creation and cleanup of a test database for our test suite. In this temporary test database, all of the project's migrations are run before our tests. This means our tests are running the latest version of the schema and are unable to verify the behavior of those very migrations because the tests cannot set up data before the migrations runor assert conditions about them.

We can teach our tests to run against those migrations with just a bit of work. This is especiallyhelpful for migrations that are going to include significant alterations to existing data.

The Django test runner begins each run by creating a new database and running all migrations init. This ensures that every test is running against the current schema the project expects, butwe'll need to work around this setup in order to test those migrations. To accomplish this, we'llneed to have the test runner step back in the migration chain just for the tests against them.

Ultimately, we're going to try to write tests against migrations that look like this:

Before explaining how to make this work, we'll break down how this test is actually written.

We're inheriting from a TestCase helper that will be written to make testing migrations possiblenamed TestMigrations and defining for this class two attributes that configure the migrationsbefore and after that we want to test. migrate_from is the last migration we expect to berun on machines we want to deploy to and migrate_to is the latest new migration we're testingbefore deploying.

Because our test is about a migration, data modifying migrations in particular, we want to dosome setup before the migration in question (0010_migration_being_tested) is run. An extrasetup method is defined to do that kind of data setup after0009_previous_migration has runbut before 0010_migration_being_tested.

Once our test runs this setup, we expect the final 0010_migration_being_tested migration tobe run. At that time, one or more test_*() methods we define can do the sort of assertionstests would normally do. In this case, we're making sure data was converted to the new schemacorrectly.

Here we've fetched a copy of this Post model's after-migration version and confirmed thevalue we set up in setUpBeforeMigration() was converted to the new structure.

Now, let's look at that TestMigrations base class that makes this possible. First, the piecesfrom Django we'll need to import to build our migration-aware test cases.

We'll be extending the TestCase class. In order to control migration running, we'lluse MigrationExecutor, which needs the database connection to operate on. Migrations aretied pretty intrinsically to Django applications, so we'll be using django.apps.apps and, inparticular, get_containing_app_config() to identify the current app our tests are running in.

We're starting with a few necessary properties.

  • app is a dynamic property that'll look up and return the name of the current app.
  • migrate_to will be defined on our own test case subclass as the name of the migrationwe're testing.
  • migrate_from is the migration we want to set up test data in, usually the latest migrationthat's currently being deployed in the project.

After insisting the test case class had defined migrate_to and migrate_from migrations,we use the internal MigrationExecutor utility to get a state of the applications as of theolder of the two migrations.

We'll use old_apps in our setUpBeforeMigration() to work with old versions of the modelsfrom this app. First, we'll run our migrations backwards to return to this original migrationand then call the setUpBeforeMigration() method.

Now that we've set up the old state, we simply run the migrations forward again. If the migrationsare correct, they should update any test data we created. Of course, we're validatingthat in our actual tests.

And finally, we store a current version of the app configuration that our tests can access anddefine a no-op setUpBeforeMigration()

Here's a complete version:

Migrations are an essential part of any Django project today, as are thorough and reliable tests. Hopefully, you can combine these two essentials now. These techniques may be particularly helpful when maintaining Django apps and libraries distributed to many users. A single project might only have one primary database in production, but distributed libraries have to have predictable, safe migrations your users can depend on. Now you can make sure of that.

Not every migration needs thorough tests! For simple non-data migrations like adding a new null column or adding a new table, you’d only be redoing the work of testing the migration tooling in Django itself, because those migrations are simple enough not to have any special impact on your data. However, this method of testing migrations can be valuable when you have data migrations or schema migrations that might be sensitive to data, such as changing constraints, renaming tables and columns, or building new indexes.

Django Test Db

Read more about testing code on the Caktus blog.

Comments are closed.