I want to debug a Django TestCase just like I would any other Python code: Simply call pdb.set_trace()
and then drop into an interactive session. When I do that, I don't see anything, as the tests are run in a different process. I'm using django-discover-runner, but my guess is that this applies to the default Django test runner.
Is it possible to drop into a pdb
session while using django-discover-runner
a) on every error / fail, AND/OR b) only when I call pdb.set_trace()
in my test code?
This answer explains that Django creates another process, and suggests using a call to rpdb2 debugger
, a part of winpdb
, but I don't want to use winpdb
, I'd rather use ipdb
.
This answer solves the problem for django-nose
by running the test command like this: ./manage.py test -- -s
, but that option's not available for django-discover-runner
.
This answer shows that I can do this with ipython
:
In [9]: %pdb
Automatic pdb calling has been turned ON
That seems like a potential option, but it seems a bit cumbersome to fire up ipython
every time I run tests.
Finally, this answer shows that nose
comes with a --pdb
flag that drops into pdb
on errors, which is what I want. Is my only option to switch to the django-nose
test runner?
I don't see any options for this in the built-in help for django-discover-runner
:
$ python manage.py help test --settings=settings.test
Usage: manage.py test [options] [appname ...]
Runs the test suite for the specified applications, or the entire site if no apps are specified.
Options:
-v VERBOSITY, --verbosity=VERBOSITY
Verbosity level; 0=minimal output, 1=normal output,
2=verbose output, 3=very verbose output
--settings=SETTINGS The Python path to a settings module, e.g.
"myproject.settings.main". If this isn't provided, the
DJANGO_SETTINGS_MODULE environment variable will be
used.
--pythonpath=PYTHONPATH
A directory to add to the Python path, e.g.
"/home/djangoprojects/myproject".
--traceback Print traceback on exception
--noinput Tells Django to NOT prompt the user for input of any
kind.
--failfast Tells Django to stop running the test suite after
first failed test.
--testrunner=TESTRUNNER
Tells Django to use specified test runner class
instead of the one specified by the TEST_RUNNER
setting.
--liveserver=LIVESERVER
Overrides the default address where the live server
(used with LiveServerTestCase) is expected to run
from. The default value is localhost:8081.
-t TOP_LEVEL, --top-level-directory=TOP_LEVEL
Top level of project for unittest discovery.
-p PATTERN, --pattern=PATTERN
The test matching pattern. Defaults to test*.py.
--version show program's version number and exit
-h, --help show this help message and exit
Django does not run tests in a separate process; the linked answer claiming it does is simply wrong. (The closest is the LiveServerTestCase
for Selenium tests, which starts up a separate thread to run the development server, but this is still not a separate process, and it doesn't prevent use of pdb). You should be able to insert import pdb; pdb.set_trace()
anywhere in a test (or in the tested code) and get a usable pdb prompt. I've never had trouble with this, and I just verified it again in a fresh project with Django 1.5.1 and django-discover-runner 1.0. If this isn't working for you, it's due to something else in your project, not due to Django or django-discover-runner.
Nose captures all output by default, which breaks import pdb; pdb.set_trace()
. The -s
option turns off output capturing. This is not necessary with the stock Django test runner or django-discover-runner, since neither of them do output-capturing to begin with.
I don't know of any equivalent to nose's --pdb
option if you're using django-discover-runner. There is a django-pdb project that provides this, but a quick perusal of its code suggests to me that it wouldn't play well with django-discover-runner; its code might give you some clues towards implementing this yourself, though.
FWIW, personally I use py.test with pytest-django rather than django-discover-runner or django-nose. And even though py.test provides a --pdb
option like nose, I don't use it; I often want to break earlier than the actual point of error in order to step through execution prior to the error, so I usually just insert import pytest; pytest.set_trace()
(importing set_trace
from pytest
does the equivalent of nose's -s
option; it turns off py.test's output capturing before running pdb) where I want it in the code and then remove it when I'm done. I don't find this onerous; YMMV.