982

The very common directory structure for even a simple Python module seems to be to separate the unit tests into their own test directory:

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

My question is simply What's the usual way of actually running the tests? I suspect this is obvious to everyone except me, but you can't just run python test_antigravity.py from the test directory as its import antigravity will fail as the module is not on the path.

I know I could modify PYTHONPATH and other search path related tricks, but I can't believe that's the simplest way - it's fine if you're the developer but not realistic to expect your users to use if they just want to check the tests are passing.

The other alternative is just to copy the test file into the other directory, but it seems a bit dumb and misses the point of having them in a separate directory to start with.

So, if you had just downloaded the source to my new project how would you run the unit tests? I'd prefer an answer that would let me say to my users: "To run the unit tests do X."

PythoNic
  • 303
  • 5
  • 13
Major Major
  • 10,286
  • 4
  • 19
  • 9
  • 6
    @EMP The proper solution when you need to set the search path is to... set the search path. What sort of solution were you expecting? – Carl Meyer Feb 17 '12 at 20:12
  • 9
    @CarlMeyer another better solution is to use the `unittest` command line interface as described in my [answer below](http://stackoverflow.com/questions/1896918/running-unittest-with-typical-test-directory-structure#24266885) so you don't have to add the directory to the path. – Pierre Jun 27 '14 at 18:30
  • 56
    Same here. I just embarked on writing my very first unit tests in for a tiny Python project and took several days trying to reason with the fact that I can't readily run a test while keeping my sources in a src directory and tests in a test directory, seemingly with any of the existing test frameworks. I'll eventually accept things, figure out a way; but this has been a very frustrating introduction. (And I'm a unit testing veteran outside Python.) – Ates Goral Mar 30 '16 at 20:43
  • Tip for coming up with a module structure: don't shadow built-in modules. `antigravity` is a CPython module and `test` is in the Python standard library. That's why I've moved to `tests`. – Joooeey Jan 27 '23 at 16:04

27 Answers27

894

The best solution in my opinion is to use the unittest command line interface which will add the directory to the sys.path so you don't have to (done in the TestLoader class).

For example for a directory structure like this:

new_project
├── antigravity.py
└── test_antigravity.py

You can just run:

$ cd new_project
$ python -m unittest test_antigravity

For a directory structure like yours:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

And in the test modules inside the test package, you can import the antigravity package and its modules as usual:

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

Running a single test module:

To run a single test module, in this case test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

Just reference the test module the same way you import it.

Running a single test case or test method:

Also you can run a single TestCase or a single test method:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

Running all tests:

You can also use test discovery which will discover and run all the tests for you, they must be modules or packages named test*.py (can be changed with the -p, --pattern flag):

$ cd new_project
$ python -m unittest discover
$ # Also works without discover for Python 3
$ # as suggested by @Burrito in the comments
$ python -m unittest

This will run all the test*.py modules inside the test package.

Pierre
  • 12,468
  • 6
  • 44
  • 63
  • 83
    `python -m unittest discover` will find and run tests in the `test` directory if they are named `test*.py`. If you named the subdirectory `tests`, use `python -m unittest discover -s tests`, and if you named the test files `antigravity_test.py`, use `python -m unittest discover -s tests -p '*test.py'` File names can use underscores but not dashes. – Mike3d0g May 18 '15 at 02:49
  • I am new to python. Can you give an example of what `antigravity.py` would contain, given the directory structure that resembles the second example. I can imagine my question is really what is the difference between package and module, but would like insight into their distinction within the context of this scenario. Thank you! – walkerrandophsmith Oct 01 '15 at 19:03
  • Note that with this solution you cannot use explicit paths in `test_antigravity.py` e.g. `import .antigravity` will not work it must be `import antigravity` – Alexander McFarlane Jun 15 '16 at 04:08
  • What happens when you have a file like "test/common.py" along side of "test/test_mything.py" that contains common test code? Do we simple reference that common.py module with a relative import? or "from test.common import blah"? – Chris Dec 12 '16 at 15:27
  • @Chris yes you can import from ``common`` using both ways, ``from .common import whatever`` or ``from test.common import whatever``. – Pierre Dec 12 '16 at 16:49
  • 20
    This fails for me on Python 3 with the error `ImportError: No module named 'test.test_antigravity'` because of a conflict with the test sub-module of the unittest library. Maybe an expert can confirm and change the answer sub-directory name to e.g., 'tests' (plural). – expz Dec 22 '16 at 21:45
  • 17
    My ```test_antigravity.py``` still throws an import error for both ```import antigravity``` and ```from antigravity import antigravity```, as well. I have both ```__init_.py``` files and I am calling ```python3 -m unittest discover``` from the ```new project``` directory. What else could be wrong? – imrek May 02 '17 at 20:11
  • 1
    @DrunkenMaster in `test/test_antigravity.py` add `import os,sys; sys.path.insert(0, os.path.abspath('..'))` – Radu Gabriel Feb 01 '18 at 13:08
  • @matanster This works in python 3. I don't even have to use discover: `python -m unittest` seems to work like discover – johnson Jun 02 '18 at 09:04
  • 33
    file `test/__init__.py` is crucial here, even if empty – Francois Aug 02 '18 at 13:32
  • 7
    @Mike3d0g not sure if you meant to imply that the directory name `test` is special...but just for the record, it isn't. :P `python -m unittest discover` works with test files in `tests/` just as well as `test/`. – ryan Oct 11 '18 at 21:31
  • 1
    @ryan It's been a while but I think I meant that tests named test*.py are special, like 'test_mygrav.py' or 'testmygrav.py', because `python -m unittest discover` will find those test files automatically. But if you've named them 'mygrav_test.py' or 'mygravtest.py', then you have to use `python -m unittest discover -p '*test.py'`. – Mike3d0g Oct 13 '18 at 13:15
  • @Pierre, Could you please update this example using Python3's namespace packages? `__init__.py` files are deprecated. – Cameron Hudson Feb 21 '20 at 16:34
  • 1
    On python3 the only difference for me was that `import antigravity` imported the module instead of the package, so I could do `from antigravity import my_object` directly. Also, you can just do `python -m unittest` to run all tests (no need to add 'discover'). All the other instructions worked as shown here. – Burrito Apr 17 '20 at 15:52
  • @Burrito, thank you for your comment, I've added `python -m unittest` to the asnwer. – Pierre Apr 22 '20 at 18:16
  • For me, running `-p '*_test.py'` did not work from the Windows command prompt, it seems like you need `-p "*_test.py"` (that's with double quotes instead of single) – Emil Bode Sep 07 '20 at 10:51
  • 1
    Do you still need to add `__init__.py`s in `python3`? – alex Mar 29 '21 at 15:55
  • 3
    This won't work if antigravity/antigravity.py imports from a module under the same antigravity directory. – RCross Sep 01 '21 at 14:45
  • 2
    @RCross This is the issue I am having. What is the solution for this? how can antigravity/antigravity.py import something under the antigravity directory and still work with the separate test/ ? – user1738539 Oct 21 '21 at 20:56
  • Still get the same module not found no matter how many __init__.py and directory rearrangments I make if the tests are not at the same level as the modules. THey cannot be in a different directory. Python3 issue? – Brian Reinhold Jul 16 '22 at 00:45
58

I've had the same problem for a long time. What I recently chose is the following directory structure:

project_path
├── Makefile
├── src
│   ├── script_1.py
│   ├── script_2.py
│   └── script_3.py
└── tests
    ├── __init__.py
    ├── test_script_1.py
    ├── test_script_2.py
    └── test_script_3.py

and in the __init__.py script of the test folder, I write the following:

import os
import sys
PROJECT_PATH = os.getcwd()
SOURCE_PATH = os.path.join(
    PROJECT_PATH,"src"
)
sys.path.append(SOURCE_PATH)

Super important for sharing the project is the Makefile, because it enforces running the scripts properly. Here is the command that I put in the Makefile:

run_tests:
    python -m unittest discover .

The Makefile is important not just because of the command it runs but also because of where it runs it from. If you would cd in tests and do python -m unittest discover ., it wouldn't work because the init script in unit_tests calls os.getcwd(), which would then point to the incorrect absolute path (that would be appended to sys.path and you would be missing your source folder). The scripts would run since discover finds all the tests, but they wouldn't run properly. So the Makefile is there to avoid having to remember this issue.

I really like this approach because I don't have to touch my src folder, my unit tests or my environment variables and everything runs smoothly.

Miles Erickson
  • 2,574
  • 2
  • 17
  • 18
Patrick Da Silva
  • 1,524
  • 1
  • 11
  • 15
55

The simplest solution for your users is to provide an executable script (runtests.py or some such) which bootstraps the necessary test environment, including, if needed, adding your root project directory to sys.path temporarily. This doesn't require users to set environment variables, something like this works fine in a bootstrap script:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

Then your instructions to your users can be as simple as "python runtests.py".

Of course, if the path you need really is os.path.dirname(__file__), then you don't need to add it to sys.path at all; Python always puts the directory of the currently running script at the beginning of sys.path, so depending on your directory structure, just locating your runtests.py at the right place might be all that's needed.

Also, the unittest module in Python 2.7+ (which is backported as unittest2 for Python 2.6 and earlier) now has test discovery built-in, so nose is no longer necessary if you want automated test discovery: your user instructions can be as simple as python -m unittest discover.

Guy Avraham
  • 3,482
  • 3
  • 38
  • 50
Carl Meyer
  • 122,012
  • 20
  • 106
  • 116
  • 1
    I put some tests in a subfolder like as "Major Major". They can run with python -m unittest discover but how can I select to run only one of them. If I run python -m unittest tests/testxxxxx then it fails for path issue. Since dicovery mode solve everything I would expect that there is another trick to solve path issue without handcoding path fix you suggest in first point – Frederic Bazin May 23 '12 at 16:07
  • 3
    @FredericBazin Don't use discovery if you only want a single test or test file, just name the module you want to run. If you name it as a module dotted-path (rather than a file path) it can figure out the search path correctly. See Peter's answer for more details. – Carl Meyer Jul 15 '14 at 01:01
  • This hack was usefull in a scenario where I had to run something like `python -m pdb tests\test_antigravity.py`. Inside pdb, I executed `sys.path.insert(0, "antigravity")` which allowed the import statement to resolve as if I was running the module. – ixe013 Apr 23 '19 at 11:46
26

I generally create a "run tests" script in the project directory (the one that is common to both the source directory and test) that loads my "All Tests" suite. This is usually boilerplate code, so I can reuse it from project to project.

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)

test/all_tests.py (from How do I run all Python unit tests in a directory?)

import glob
import unittest

def create_test_suite():
    test_file_strings = glob.glob('test/test_*.py')
    module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
    suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

With this setup, you can indeed just include antigravity in your test modules. The downside is you would need more support code to execute a particular test... I just run them all every time.

Community
  • 1
  • 1
stw_dev
  • 842
  • 11
  • 14
  • 2
    I also wanted *a `run tests` script in the project directory* and found [a lot cleaner way](https://www.internalpointers.com/post/run-painless-test-suites-python-unittest) to do it. Highly recommended. – z33k Mar 26 '18 at 19:14
19

From the article you linked to:

Create a test_modulename.py file and put your unittest tests in it. Since the test modules are in a separate directory from your code, you may need to add your module’s parent directory to your PYTHONPATH in order to run them:

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py

Finally, there is one more popular unit testing framework for Python (it’s that important!), nose. nose helps simplify and extend the builtin unittest framework (it can, for example, automagically find your test code and setup your PYTHONPATH for you), but it is not included with the standard Python distribution.

Perhaps you should look at nose as it suggests?

Topher
  • 596
  • 4
  • 15
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 6
    Yes this works (for me), but I'm really asking for the simplest instructions that I can give users to my module to get them to run the tests. Modifying the path might actually be it, but I'm fishing for something more straight-forward. – Major Major Dec 13 '09 at 16:39
  • 6
    So what does your python path look like after you've worked on a hundred projects? Am I supposed to manually go in and clean up my path? If so this is an odious design! – jeremyjjbrown Jun 21 '14 at 23:16
16

I had the same problem, with a separate unit tests folder. From the mentioned suggestions I add the absolute source path to sys.path.

The benefit of the following solution is, that one can run the file test/test_yourmodule.py without changing at first into the test-directory:

import sys, os
testdir = os.path.dirname(__file__)
srcdir = '../antigravity'
sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir)))

import antigravity
import unittest
per1234
  • 878
  • 5
  • 13
admirableadmin
  • 2,669
  • 1
  • 24
  • 41
13

I noticed that if you run the unittest command line interface from your "src" directory, then imports work correctly without modification.

python -m unittest discover -s ../test

If you want to put that in a batch file in your project directory, you can do this:

setlocal & cd src & python -m unittest discover -s ../test
Alan L
  • 1,785
  • 19
  • 28
  • 1
    Out of all the answers on this page, this is the only one that worked for me. I suspect other answers are missing some vital info. – RCross Sep 01 '21 at 14:33
  • 1
    It's hilariously stupid that we have to do this. But what can you do.. It's the most simple and easy solution – musicman Oct 03 '21 at 21:52
  • The other answers are relying on the python import system. With this answer you are specifying the path for your tests and I think the test runner modifies the path before running the test. – ta32 Feb 27 '22 at 04:46
12

Solution/Example for Python unittest module

Given the following project structure:

ProjectName
 ├── project_name
 |    ├── models
 |    |    └── thing_1.py
 |    └── __main__.py
 └── test
      ├── models
      |    └── test_thing_1.py
      └── __main__.py

You can run your project from the root directory with python project_name, which calls ProjectName/project_name/__main__.py.


To run your tests with python test, effectively running ProjectName/test/__main__.py, you need to do the following:

1) Turn your test/models directory into a package by adding a __init__.py file. This makes the test cases within the sub directory accessible from the parent test directory.

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        

2) Modify your system path in test/__main__.py to include the project_name directory.

# ProjectName/test/__main__.py

import sys
import unittest

sys.path.append('../project_name')

loader = unittest.TestLoader()
testSuite = loader.discover('test')
testRunner = unittest.TextTestRunner(verbosity=2)
testRunner.run(testSuite)

Now you can successfully import things from project_name in your tests.

# ProjectName/test/models/test_thing_1.py    

import unittest
from project_name.models import Thing1  # this doesn't work without 'sys.path.append' per step 2 above

class Thing1TestCase(unittest.TestCase):

    def test_thing_1_init(self):
        thing_id = 'ABC'
        thing1 = Thing1(thing_id)
        self.assertEqual(thing_id, thing.id)
Derek Soike
  • 11,238
  • 3
  • 79
  • 74
11

if you run "python setup.py develop" then the package will be in the path. But you may not want to do that because you could infect your system python installation, which is why tools like virtualenv and buildout exist.

Tom Willis
  • 5,250
  • 23
  • 34
6

If you use VS Code and your tests are located on the same level as your project then running and debug your code doesn't work out of the box. What you can do is change your launch.json file:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "program": "${file}",
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.env",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }    
    ]
}

The key line here is envFile

"envFile": "${workspaceRoot}/.env",

In the root of your project add .env file

Inside of your .env file add path to the root of your project. This will temporarily add

PYTHONPATH=C:\YOUR\PYTHON\PROJECT\ROOT_DIRECTORY

path to your project and you will be able to use debug unit tests from VS Code

Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
6

Python 3+

Adding to @Pierre

Using unittest directory structure like this:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

To run the test module test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

Or a single TestCase

$ python -m unittest test.test_antigravity.GravityTestCase

Mandatory don't forget the __init__.py even if empty otherwise will not work.

imbr
  • 6,226
  • 4
  • 53
  • 65
5

Use setup.py develop to make your working directory be part of the installed Python environment, then run the tests.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • This gets me an `invalid command 'develop'` and this option isn't mentioned if I ask for `setup.py --help-commands`. Does there need to be something in the `setup.py` itself for this to work? – Major Major Dec 13 '09 at 16:43
  • It's OK - the problem was I was missing an `import setuptools` from my `setup.py` file. But I guess that does go to show that this won't work all the time for other people's modules. – Major Major Dec 13 '09 at 16:54
  • 2
    If you have [pip](https://python-packaging-user-guide.readthedocs.org/en/latest/quickstart.html#install-the-tools), you can use that to install your package in ["editable" mode](http://www.pip-installer.org/en/latest/usage.html#pip-install): `pip install -e .` This likewise adds the package to the Python environment without copying the source, allowing you to continue to edit it where it lies. – Eric Smith Feb 04 '14 at 23:39
  • `pip install -e .` is the exact same thing as `python setup.py develop`, it just monkeypatches your `setup.py` to use setuptools even if it doesn't actually, so it works either way. – Carl Meyer Jul 15 '14 at 00:57
5

You can't import from the parent directory without some voodoo. Here's yet another way that works with at least Python 3.6.

First, have a file test/context.py with the following content:

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

Then have the following import in the file test/test_antigravity.py:

import unittest
try:
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity

Note that the reason for this try-except clause is that

  • import test.context fails when run with "python test_antigravity.py" and
  • import context fails when run with "python -m unittest" from the new_project directory.

With this trickery they both work.

Now you can run all the test files within test directory with:

$ pwd
/projects/new_project
$ python -m unittest

or run an individual test file with:

$ cd test
$ python test_antigravity

Ok, it's not much prettier than having the content of context.py within test_antigravity.py, but maybe a little. Suggestions are welcome.

tjk
  • 339
  • 3
  • 8
  • The trick with `context.py` is quite good: it allows to import main modules from tests when unit tests are ran via command line or in VSCode. – AntonK Feb 17 '23 at 11:47
4

It's possible to use wrapper which runs selected or all tests.

For instance:

./run_tests antigravity/*.py

or to run all tests recursively use globbing (tests/**/*.py) (enable by shopt -s globstar).

The wrapper can basically use argparse to parse the arguments like:

parser = argparse.ArgumentParser()
parser.add_argument('files', nargs='*')

Then load all the tests:

for filename in args.files:
    exec(open(filename).read())

then add them into your test suite (using inspect):

alltests = unittest.TestSuite()
for name, obj in inspect.getmembers(sys.modules[__name__]):
    if inspect.isclass(obj) and name.startswith("FooTest"):
        alltests.addTest(unittest.makeSuite(obj))

and run them:

result = unittest.TextTestRunner(verbosity=2).run(alltests)

Check this example for more details.

See also: How to run all Python unit tests in a directory?

kenorb
  • 155,785
  • 88
  • 678
  • 743
3

You should really use the pip tool.

Use pip install -e . to install your package in development mode. This is a very good practice, recommended by pytest (see their good practices documentation, where you can also find two project layouts to follow).

yanlend
  • 450
  • 3
  • 10
squid
  • 2,597
  • 1
  • 23
  • 19
  • Why downvote this answer? I read the accepted answer and while it was not bad, `pytest` is way better to run tests, because of the console output you get, in color, with stack trace info and detailed assertion error information. – aliopi Jul 26 '17 at 21:38
3

Following is my project structure:

ProjectFolder:
 - project:
     - __init__.py
     - item.py
 - tests:
     - test_item.py

I found it better to import in the setUp() method:

import unittest
import sys    

class ItemTest(unittest.TestCase):

    def setUp(self):
        sys.path.insert(0, "../project")
        from project import item
        # further setup using this import

    def test_item_props(self):
        # do my assertions

if __name__ == "__main__":
    unittest.main()
rolika
  • 381
  • 1
  • 10
3

What's the usual way of actually running the tests

I use Python 3.6.2

cd new_project

pytest test/test_antigravity.py

To install pytest: sudo pip install pytest

I didn't set any path variable and my imports are not failing with the same "test" project structure.

I commented out this stuff: if __name__ == '__main__' like this:

test_antigravity.py

import antigravity

class TestAntigravity(unittest.TestCase):

    def test_something(self):

        # ... test stuff here


# if __name__ == '__main__':
# 
#     if __package__ is None:
# 
#         import something
#         sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
#         from .. import antigravity
# 
#     else:
# 
#         from .. import antigravity
# 
#     unittest.main()
aliopi
  • 3,682
  • 2
  • 29
  • 24
1

If you have multiple directories in your test directory, then you have to add to each directory an __init__.py file.

/home/johndoe/snakeoil
└── test
    ├── __init__.py        
    └── frontend
        └── __init__.py
        └── test_foo.py
    └── backend
        └── __init__.py
        └── test_bar.py

Then to run every test at once, run:

python -m unittest discover -s /home/johndoe/snakeoil/test -t /home/johndoe/snakeoil

Source: python -m unittest -h

  -s START, --start-directory START
                        Directory to start discovery ('.' default)
  -t TOP, --top-level-directory TOP
                        Top level directory of project (defaults to start
                        directory)
Qlimax
  • 5,241
  • 4
  • 28
  • 31
0

This BASH script will execute the python unittest test directory from anywhere in the file system, no matter what working directory you are in.

This is useful when staying in the ./src or ./example working directory and you need a quick unit test:

#!/bin/bash

this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

No need for a test/__init__.py file to burden your package/memory-overhead during production.

John Greene
  • 2,239
  • 3
  • 26
  • 37
0

This way will let you run the test scripts from wherever you want without messing around with system variables from the command line.

This adds the main project folder to the python path, with the location found relative to the script itself, not relative to the current working directory.

import sys, os

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

Add that to the top of all your test scripts. That will add the main project folder to the system path, so any module imports that work from there will now work. And it doesn't matter where you run the tests from.

You can obviously change the project_path_hack file to match your main project folder location.

chasmani
  • 2,362
  • 2
  • 23
  • 35
0

A simple solution for *nix based systems (macOS, Linux); and probably also Git bash on Windows.

PYTHONPATH=$PWD python test/test_antigravity.py

print statement easily works, unlike pytest test/test_antigravity.py. A perfect way for "scripts", but not really for unittesting.

Of course, I want to do a proper automated testing, I would consider pytest with appropriate settings.

Polv
  • 1,918
  • 1
  • 20
  • 31
0

With cwd being the root project dir (new_project in your case), you can run the following command without __init__.py in any directory:

python -m unittest discover -s test

But you need import in test_antigravity.py as:

from antigravity import antigravity.your_object

instead of:

import antigravity.your_object

If you don't like from antigravity clause, you might like Alan L's answer.

catwith
  • 875
  • 10
  • 13
0

Create a conftest.py file in your root or test directory.

import sys
# Runs before all tests and imports when you run pytest
def pytest_configure() -> None:
    sys.path.append(__file__) # or __file__.parent for the above directory, or __file__ / 'src', etc.

How to run the tests: call pytest from any directory.


Note: I think the best way 99% of the time is to just tell people to update their PYTHONPATH (you already made them install python+all of the requirements, just tell them to update their .bashrc).


Note2: pytest can run unittest. (and I think it's generally more useful). unittest does not support conftest.py. You can use an __init__.py instead but in my experience, this file doesn't always run first, other files sometimes get imported before it is called depending on structure and how the command is called.

Alex Li
  • 1
  • 1
0

Noobs issues..

The file name issue

I've been googling around for an answer to this issue too but nothing worked for my case.

I'm using Windows and the reason unittest wasn't able to discover the modules is because I used hyphen on the file name. Renaming the file name with underscores fixed the issue.

See this stackoverflow issue

carmel
  • 902
  • 7
  • 24
-1

If you are looking for a command line-only solution:

Based on the following directory structure (generalized with a dedicated source directory):

new_project/
    src/
        antigravity.py
    test/
        test_antigravity.py

Windows: (in new_project)

$ set PYTHONPATH=%PYTHONPATH%;%cd%\src
$ python -m unittest discover -s test

See this question if you want to use this in a batch for-loop.

Linux: (in new_project)

$ export PYTHONPATH=$PYTHONPATH:$(pwd)/src  [I think - please edit this answer if you are a Linux user and you know this]
$ python -m unittest discover -s test

With this approach, it is also possible to add more directories to the PYTHONPATH if necessary.

pj.dewitte
  • 472
  • 3
  • 10
-1

I think that the method outlined in https://docs.python-guide.org/writing/structure/ is very clean:

(quoted here verbatim from that article)

To give the individual tests import context, create a tests/context.py file:

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import sample

Then, within the individual test modules, import the module like so:

from .context import sample

This will always work as expected, regardless of installation method.

Joseph Bolton
  • 36
  • 1
  • 5
-2

unittest in your project have setup.py file. try:

python3 setup.py build

and

python3 setup.py develop --user

do the work of config paths an so on. try it!

Valentin Vignal
  • 6,151
  • 2
  • 33
  • 73