Can't debug Django unit tests within Visual Studio Code

Question:

I want to be able to run and debug unit tests for a Django project from within Visual Studio Code. I am an experienced developer, but fairly new to both Django and Visual Studio Code.

The crux of the problem is that either the tests are undiscoverable within Visual Studio Code, or if they are discoverable, I get a ConnectionHandler exception when I run them. I provide details for both of these situations below.

I apologize for the length of this question, but I thought I should list the many solutions to the problem that I have tried unsuccessfully. I also think that it might be useful to put all of them into one place, rather than have them scattered all over StackOverflow and the rest of the Internet.

Best Solution So Far

The best solution I have found is at Problem with Django app unit tests under Visual Studio Code . It involves putting these four lines into an __init__.py file:

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_app_project.settings")

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

This works in my simple tutorial projects, described below, but in a real application some of the tests fail. It looks as though either Django isn’t running the database models in isolation from the other tests, or that the tests’ tearDown( ) method isn’t being called. Also, it’s not clear which __init__.py file to put these lines in. Finally, this solution causes unit tests to fail when run from the command line.

Other Solutions Tried

The StackOverflow question at How to fix "TypeError: argument of type 'ConnectionHandler' is not iterable" when running a django test? seems to be similar to the ConnectionHandler aspect of my problem, but no one has proposed a solution there.

I’ve found a Visual Studio Code extension that is supposed to resolve these problems: Django Test Runner (https://marketplace.visualstudio.com/items?itemName=Pachwenko.django-test-runner). It currently has an issue at https://github.com/Pachwenko/VSCode-Django-Test-Runner/issues/5 which describes the problem I’m having with the extension.

This blog says you can run Django unit tests in VSC with pytest: https://technowhisp.com/django-unit-testing-in-vscode/ . But even after carefully following his steps, I can’t get pytest to discover the unit tests within Visual Studio Code.

The StackOverflow question at Django Unit Testing in Visual Studio 2017 or 2019: has no answer. The code the questioner presents as a partial solution is ugly and bloated. The question is tagged as a possible duplicate of Django Unit Testing in Visual Studio 2017 or 2019: , but that question has no answer either. Other questions on StackOverflow aren’t related to Django or even Python.

My Setup

The rest of this question describes my environment and the exact nature of the problems I am having. I am working with an existing application but have managed to reproduce the problem on two tutorial projects–the one provided by the Django team in their “Writing your first Django app” online tutorial at https://docs.djangoproject.com/en/3.0/intro/ , and a second tutorial offered by the Visual Studio Code team at https://code.visualstudio.com/docs/python/tutorial-django . Structurally, the difference between the two projects is that the second has the manage.py script in the same directory as the project root, while the first project has the script one directory below the project root.

I am able to run the tests from the command line with no problem. I want to be able to run them from within Visual Studio Code because, sometimes, I want to run them through the debugger.

I am using python 3.7, Django 3.0.3, Visual Studio Code 1.42.1, Windows 10, the Windows Subsystem for Linux and the unittest framework.

As for my project, I will describe the setup for the first tutorial, since that is most similar to the way my real project is set up. The root directory is called mysite. Cleaned-up output from the tree command gives this:

.
└── mysite
    ├── db.sqlite3
    ├── manage.py
    ├── mysite
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── polls
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── migrations
        ├── models.py
        ├── mydate.py
        ├── tests.py
        ├── urls.py
        └── views.py

Here is my VSC settings.json file:

{
    "python.pythonPath": "venv/bin/python",
    "python.testing.unittestArgs": [
        "-v",
        "-s",
        // "/full/path/to/mysite",
        "./mysite",
        // "mysite",
        "-p",
        "test*.py"
    ],
    "python.testing.pytestEnabled": false,
    "python.testing.nosetestsEnabled": false,
    "python.testing.unittestEnabled": true
}

The file shows two lines commented out after the “-s” argument. I get the same behavior no matter which of these three lines I use for the path.

ConnectionHandler Error within VSC

In Visual Studio Code, my polls/tests.py file looks like this:

enter image description here

As you can see, the “Run Test” and “Debug Test” buttons indicate the test is discoverable. With the unittest import, the test runs correctly within VSC, but with the django.test import I get a ConnectionHandler error with the following message stack:

======================================================================
ERROR: setUpClass (polls.tests.TestDjango)----------------------------------------------------------------------
Traceback (most recent call last):  
File "/home/jkurlandski/workspace/randd/djangoprojs/mysite/venv/lib/python3.7/site-packages/django/test/testcases.py", line 1123, in setUpClass    super().setUpClass()  
File "/home/jkurlandski/workspace/randd/djangoprojs/mysite/venv/lib/python3.7/site-packages/django/test/testcases.py", line 197, in setUpClass    cls._add_databases_failures()  
File "/home/jkurlandski/workspace/randd/djangoprojs/mysite/venv/lib/python3.7/site-packages/django/test/testcases.py", line 218, in _add_databases_failures    cls.databases = cls._validate_databases()  File "/home/jkurlandski/workspace/randd/djangoprojs/mysite/venv/lib/python3.7/site-packages/django/test/testcases.py", line 204, in _validate_databases    if alias not in connections:
TypeError: argument of type 'ConnectionHandler' is not iterable
----------------------------------------------------------------------
Ran 0 tests in 0.001s

Import Problems within VSC

In mysite/mysite/polls/models.py I created the same Question and Choice models described in the Django tutorial referenced above. When I try to import them, however, the tests are no longer discoverable–the “Run” and “Debug” buttons disappear in VSC, and I get a message saying they are not discoverable.

from django.test import TestCase
# from unittest import TestCase

# not discoverable:
# from .models import Choice
# not discoverable: 
# from polls.models import Choice

In VSC, the use of from polls.models import Choice creates an “unresolved import ‘polls.models’ warning and causes the import statement to become underlined in yellow. The from .models import Choice import does not cause the warning. Since the test is not discoverable I can’t run it, or debug it, within VSC. (But note, as I’ve said, that from the command line the test runs correctly.)

As an experiment, I created the file mydate.py in polls, the same directory as models.py, containing a function called getNow( ). I can import this file into tests.py without losing test discoverability, but running the test causes the same ConnectionHandler error described above.

from django.test import TestCase
# from unittest import TestCase

# discoverable, but TypeError: argument of type 'ConnectionHandler' is not iterable: 
from polls.mydate import getNow
# discoverable, but TypeError: argument of type 'ConnectionHandler' is not iterable: 
# from .mydate import getNow

ConnectionHandler Error from Command Line

As I’ve said, I can run the unit tests from the command line with python manage.py test. However, I can replicate the ConnectionHandler error by running python -m unittest from the manage.py directory.

Thanks in advance for any help you guys can give me.

Asked By: Jerry K.

||

Answers:

You need to load the django configuration previous to run any test, for that reason the code in __init__.py file.

If pytest is an option for you, you have to install pytest and pytest-django

pip install pytest pytest-django
  1. create a pytest configuration (ex: pytest.ini) at the same level of manage.py file with the following content.

    [pytest]
    DJANGO_SETTINGS_MODULE=mysite.settings
    python_files=test*.py
    

    The configuration DJANGO_SETTINGS_MODULE is the import needed for the project setting (settings.py file). By default, the file tests.py is created for every app when you use python manage.py startapp app_name, but this file doesn’t match with the default pytest file pattern, for that reason you have to add the python_files options if you want to use the default file name.

  2. Update the setting.json configuration file with:

    {
        "python.pythonPath": "venv/bin/python",
        "python.testing.pytestArgs": [
            "mysite",
            "-s",
            "-vv"
        ],
        "python.testing.pytestEnabled": true,
        "python.testing.nosetestsEnabled": false,
        "python.testing.unittestEnabled": false
    }
    

About the error unresolved import 'polls.models', you need to configure the Python extension on VSCode creating a .env file in the root of your project with PYTHONPATH=source_code_folder (for more information VS Code Python Environment):

PYTHONPATH=mysite/

Finally, your project structure should be

.  <-- vs code root
├── .env  <-- file created with the PYTHONPATH entry
└── mysite
    ├── pytest.ini  <-- pytest configuration
    ├── db.sqlite3
    ├── manage.py
    ├── mysite
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── polls
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── migrations
        ├── models.py
        ├── mydate.py
        ├── tests.py
        ├── urls.py
        └── views.py
Answered By: Motakjuq