Enable setup.py test in your Django apps

Setuptools comes with a way to run the tests on your application. This allows the user of your software to download it, and run python setup.py test and check to see if the tests in your application pass. This is really useful for distribution, because the user doesn’t need to know or care how to run your tests (nose, django, unittest, py.test, or whatever else), and can simply see if they pass.

To do this, you simply define a test_suite variable in the setup() function of your setup.py. This argument is a callable that should return a test class. However, since Django has it’s own test runner, we have to point this at a simple test runner that we construct, and allow that to run the tests. This is because we must set a couple of environmental things, like the settings module and PYTHONPATH.

I did this with my test_utils project, you can see the commit here, but basically I simply added this line to my setup.py:

test_suite = "test_project.runtests.runtests",

Then put this in the file test_project/runtests:

#This file mainly exists to allow python setup.py test to work.
import os, sys
os.environ['DJANGO_SETTINGS_MODULE'] = 'test_project.settings'
test_dir = os.path.dirname(__file__)
sys.path.insert(0, test_dir)

from django.test.utils import get_runner
from django.conf import settings

def runtests():
    test_runner = get_runner(settings)
    failures = test_runner([], verbosity=1, interactive=True)
    sys.exit(failures)

if __name__ == '__main__':
    runtests()

Note that there is a bit of path and settings hackery, this is to enable Django to run correctly, with the test_project in the PYTHONPATH. However, now if you want to run the tests, you simple do a python setup.py test. You can also do a python runtests.py to have the same outcome. You could also have the runtests function simply be a call_command('test'), but without the explicit sys.exit, setuptools complains that it hasn’t been given a TestCase back.

This has a couple drawbacks in that it simply runs the entire test suite. You can pass in a Test Suite to setuptools, but that isn’t how the tests are organized in most Django apps. However, if you want to run specific tests, you can still test things the regular way through manage.py test. Presumably there is a better way to do this, but it’s good to at least have a hacky way until a better way emerges.

There is a lot of value in providing a standard interface to running the tests on your application though. This allows the distribution tools (pip and setuptools) to run the tests on your application if they’d like. In the perl universe, CPAN runs the tests on apps before installing them, and quitting if they fail. If more people started making setup.py test work, we could hopefully add this ability to Python’s distribution tools, and make the world a happier place with better working software.



Hey there! I'm Eric and I work on communities in the world of software documentation. Feel free to email me if you have comments on this post!