Testing Django apps is not as straightforward as testing whole Django projects. After all, Django apps are meant to be part of a project, and they cannot survive on their own. So how do you go about it? There are many ways to do it (as usual), ranging from testing only the parts that aren't related to Django, to scaffolding whole Django projects just for the purpose of testing.

Here I will show you how to write a simple script that would allow you to run test suites for pluggable apps without having to scaffold entire projects, while still allowing you to have a full Django environment akin to ones in which you'd run your apps. I find this method to be the most maintainable and easiest to understand.

First off, you will need to set up a virtualenv for your pluggable app. Let's assume that you have a pluggable app called django-foo. Here is the directory layout:

foo/
tests/
README.rst
setup.py
MANIFEST.in
NEWS
LICENSE

The pluggable app resides in the foo directory. All our tests will live in tests. You can also see a few files related to packaging such as the setup module, the README file, MANIFEST file, license and changelog.

For the script I have in mind, we'll use nose. We'll also need Django itself.

Django==1.5
nose==1.2.1

Add any other testing tools you may need such as mock. Save and install the requirements.

Next create a script called runtests.py in the root of the source directory. First thing first, we need to configure Django before we run the test suite.

#!/usr/bin/env python

from django.conf import settings, global_settings

if not settings.configured:
# Configure your project here
settings.configure(
INSTALLED_APPS=[
'foo',
],
DEBUG=False,
SITE_ID=1,
)

The settings we use here are minimal. Obviously, this is a full settings module, so you are free to add any settings you think you'll need in your real Django module, or load settings from another module, etc.

Next, we need to add the part that actually runs the tests:

#!/usr/bin/env python

import sys
from os.path import dirname, abspath

from django.conf import settings, global_settings

from nose.plugins.plugintest import run_buffered as run

if not settings.configured:
# Configure your project here
settings.configure(
INSTALLED_APPS=[
'foo',
],
DEBUG=False,
SITE_ID=1,
)

def runtests():
# Add app's source directory to sys.path
parent_dir = dirname(abspath(__file__))
sys.path.insert(0, parent)

# Run tests with whatever argument was passed to the script
run(argv=sys.argv)

if __name__ == '__main__':
runtests()

Now, with this script, you are able to run all tests in your source directory using nose. Just run it with python runtests.py and nose will find and run all tests that it finds. You can also pass arguments to nose so it runs only the tests you are interested in (ex: python runtests.py tests.test_my_wheels).

As for tests themselves, you are free to write them as you would normally.