Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use pdb.set_trace() in a Django unittest?

Tags:

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.

The question:

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?

Some research:

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 
like image 458
Brian Dant Avatar asked Jun 20 '13 13:06

Brian Dant


People also ask

What is PDB in Django?

Django pdb. pdb -- short for "Python Debugger" -- is a Python core package designed to interactively debug source code. With Python pdb you can inspect the line by line execution of any Python application.

Does Django use Pytest or Unittest?

Django's unit tests use a Python standard library module: unittest . This module defines tests using a class-based approach.

What does PDB set Trace do?

set_trace() function. Wherever you put this line in your code, the Python interpreter will stop and present an interactive debugger command prompt. From here, you can run any arbitrary Python code, or issue pdb commands to step through or even into the code by using next and step, respectively.


1 Answers

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.

like image 133
Carl Meyer Avatar answered Oct 06 '22 15:10

Carl Meyer