pythonvisual-studio-codepython-unittestproject-structure

Why Can't VS Code Directly Run Python unittest .py's Or Discover In Testing Tab


I was having issues with this when learning Python unittest for a larger project, so I reproduced it for a simple boilerplate project. I make a folder structure like this and have the project open in VS Code from the top directory.

unittestproject
├──examplepackage
├────__init__.py
├──test
├────__init__.py

I make a virtual environment by pressing Ctrl+Shift+P and using Python: Create Environment from the command palette to make a .venv Virtual environment for the project. I don't think that's relevant to the issue but I'll mention it.

I make a py file in /examplepackage called add.py that just contains a single function that adds two arguments and returns the result.

I make a py file in /test called testadd.py with the following contents...

import unittest
from examplepackage import add

class TestAdd(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add.add(1,2),3)
    
if __name__ == "__main__":
    unittest.main()

I can run this test successfully in the following ways from the terminal, while located in the unittestproject directory.

python -m unittest
python -m unittest test/testadd.py
python -m unittest test.testadd

This is good, but if I try to directly run testadd.py using the play button in the upper right corner, I get a ModuleNotFoundError...

$ f:/Python/unittestproject/.venv/Scripts/python.exe
f:/Python/unittestproject/test/testadd.py
Traceback (most recent call last):
  File "f:\Python\unittestproject\test\testadd.py", line 2, in <module>
    from examplepackage import add
ModuleNotFoundError: No module named 'examplepackage'

Also, whenever I try to set up tests in the "Testing" tab, which creates the following settings.json file...

{
    "python.testing.unittestArgs": [
        "-v",
        "-s",
        "./test",
        "-p",
        "test*.py"
    ],
    "python.testing.pytestEnabled": false,
    "python.testing.unittestEnabled": true
}

I keep seeing an error that confusingly relates to PyTest even though I specified that I want to use unittest, NOT PyTest.

[error] f:\Python\unittestproject\.venv\Scripts\python.exe: No module named pytest
[error] Subprocess exited unsuccessfully with exit code 1 and signal null on workspace f:\Python\unittestproject.
[error] Subprocess exited unsuccessfully with exit code 1 and signal null on workspace f:\Python\unittestproject. Creating and sending error discovery payload
[error] pytest test discovery error for workspace:  f:\Python\unittestproject

And so my questions are:

I've tried to resolve these on my own and with other forum questions over the last few hours, but what others are saying should work is not working for me. One example, appending "project." to the front of the package import does not resolve either. I have also tried adding __init__.py to the top directory.

EDIT: I don't know why this is, maybe it's a bug or something. But I closed VS Code and came back to it later, and tests loaded up in the Testing tab no problem. It was using unittest instead of PyTest and running them worked fine. This might be a bug.


Solution

  • I think I've discovered all the answers:

    It seems to all come down to the python path. If I run python -m unittest from the unittestproject directory, the first entry in the python path becomes drive:\path\to\unittestproject. But if I run the script directly, the first entry in the path is drive:\path\to\unittestproject\test. So if I want to be able to run the test both ways, I need to add this to the top of my unit test script

    import sys, os
    sys.path.append(os.path.dirname(sys.path[0]))
    

    This includes the parent directory in the path so modules in adjacent packages can be seen.

    If you open a test*.py script under /test in VS code directly, without having any folder open in the "Explorer" panel, the IntelliSense cannot actually resolve the other package either. However, as soon as you open the full project folder in that panel, it resolves. So again, it comes down to the path that is being used to determine whether the package exists.

    This is a bit of a mystery but it looks like a bug. I loaded up VS Code later and it just worked. Since then, I haven't seen any mysterious references to PyTest and it consistently uses unittest.