How I set up VSCode for Python (tests, coverage, profiling)
Tags: python, testing, profiling, coverage
Published: 2021-01-07 11:31:11

# 1. Install the Python Extension
It can be found [here](https://marketplace.visualstudio.com/items?itemName=ms-python.python) 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:
```python
project_directory
+-- my_module
|   +-- __init__.py
|   +-- __main__.py
|   +-- script.py
+-- tests
    +-- __init__.py
    +-- test_script.py
```


# 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:

```json
"configurations": [    
        {
            "name": "My Python Module",
            "type": "python",
            "request": "launch",
            "module": "my_module"
        }
    ]
```

# 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
```python
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)
```

## 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](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-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`:
```python
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()
```

You can then use a tool like [SnakeViz](https://jiffyclub.github.io/snakeviz/) to visualize the results in your browser by running `snakeviz program.prof`.

# 5. Other useful extension
* [todo tree] (https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree) organizes your todos in a neat tree structure, the todo keywords can be customized