DEV Community

yamakanto
yamakanto

Posted on

How I set up VSCode for Python (tests, coverage, profiling)

1. Install the Python Extension

It can be found here or by searching for python in the extensions section.

2. Organize your Project

First off you should use a new virtual environment.
Using the virtualenv command line tool you can use virtualenv -p3.9 .venv3.9 to define a new Python 3.9 environment. VSCode should show a prompt asking you whether you want to switch to this environment. Choose 'yes'.

For the project itself I use the following structure:

project_directory
+-- my_module
|   +-- __init__.py
|   +-- __main__.py
|   +-- script.py
+-- tests
    +-- __init__.py
    +-- test_script.py
Enter fullscreen mode Exit fullscreen mode

3. Running your Code

As the project structure uses __init__.py files we need to run the project as a module: python -m my_module. You can also adapt the vscode debug configurations to this module-based approach:

In the run/debug tab click on 'add configuration', then you can add or edit entries to follow for example the following structure:

"configurations": [    
        {
            "name": "My Python Module",
            "type": "python",
            "request": "launch",
            "module": "my_module"
        }
    ]
Enter fullscreen mode Exit fullscreen mode

4. Testing

I use pytest for testing in vscode. Make sure to configure tests for the python extension and to choose pytest as well as to configure pytest to use your naming convention. For example if you call your test files something like test_script.py where the script part indicates what you are testing you should choose the test name format test_*.py.

4.1 Defining a test

I use the following structure

import unittest
from my_module.script import add_func

class TestScript(unittest.TestCase):
    def test_add(self):
        result = add_func(1,2)
        self.assertEqual(result, 3)
Enter fullscreen mode Exit fullscreen mode

4.2 Running tests

You can run the tests from the terminal via pytest.
Alternatively you can use the vscode extension's icon and run all the tests by clicking on the run symbol in the corresponding sidebar tab.

4.3 Test Coverage

  • run pytest --cov=main_module --cov-report=xml tests
    • --cov=main_module enables coverage for the main_module
    • --cov-report=xml generates a coverage.xml file that can be used with the vscode extension coverage gutters.
  • I don't pass these arguments to pytest via vscode as that seems to break the debugging feature.

5. Profiling

I use the following in __main__.py:

import cProfile
from pstats import Stats, SortKey

from my_module.script import add_func


def run_script():
    print(add_func(2,1))


if __name__ == '__main__':
    do_profiling = True
    if do_profiling:
        with cProfile.Profile() as pr:
            run_script()

        with open('profiling_stats.txt', 'w') as stream:
            stats = Stats(pr, stream=stream)
            stats.strip_dirs()
            stats.sort_stats('time')
            stats.dump_stats('.prof_stats')
            stats.print_stats()
    else:
        start_game()
Enter fullscreen mode Exit fullscreen mode

You can then use a tool like SnakeViz to visualize the results in your browser by running snakeviz program.prof.

5. Other useful extension

  • todo tree organizes your todos in a neat tree structure, the todo keywords can be customized

Latest comments (1)

Collapse
 
basejumpa profile image
Alexander Mann-Wahrenberg

Thx for sharing this!

There's a solution to integrate coverage to VSCode while not breaking debugging of tests. A found it at github.com/microsoft/vscode-python...

Here's my /setup.cfg:

[tool:pytest]
testpaths = src/
addopts = -n4 --cov=src --cov-report=term --cov-report=lcov:.coverage-dir/lcov.info --cov-report=html:.coverage-dir/html
required_plugins = pytest-cov pytest-xdist

[coverage:run]
omit = **/*/tests/*
branch = true
data_file = .coverage

[coverage:report]
fail_under = 100
Enter fullscreen mode Exit fullscreen mode

Here's my /.vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Debug Tests",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "purpose": ["debug-test"],
            "console": "integratedTerminal",
            "justMyCode": false,
            "env": {
                "PYTEST_ADDOPTS": "--no-cov"
            },
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode