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.

Here you can find the updated official documentation of discovery.

Answer from Pierre on Stack Overflow
Top answer
1 of 16
949

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.

Here you can find the updated official documentation of discovery.

2 of 16
76

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.

🌐
GitHub
gist.github.com β€Ί tasdikrahman β€Ί 2bdb3fb31136a3768fac
Typical Directory structure for python tests Β· GitHub
Clone this repository at <script src="https://gist.github.com/tasdikrahman/2bdb3fb31136a3768fac.js"></script> Save tasdikrahman/2bdb3fb31136a3768fac to your computer and use it in GitHub Desktop. ... 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). ... new_project β”œβ”€β”€ antigravity β”‚ β”œβ”€β”€ __init__.py # make it a package β”‚ └── antigravity.py └── test β”œβ”€β”€ __init__.py # also make test a package └── test_antigravity.py
Discussions

What is the best project structure for a Python application? - Stack Overflow
Each egg has a separate set of tests, kept in its PROJECT_ROOT/src//tests directory. I personally prefer to use py.test to run them. Where do you put non-Python data such as config files? More on stackoverflow.com
🌐 stackoverflow.com
Arguments against separating `test` from `src` in a python package?
I'm more familiar with Node rather than Python but I separate my test directory from my source directory because I can easily exclude the test directory when publishing the package. So when it is used in another project it has a smaller footprint. It just makes more sense if you are going to exclude it, leave it out of the src directory. If I had to guess, that is why Python recommends it. If I'm wrong I'd love to know why. Hope this helps! More on reddit.com
🌐 r/Python
67
174
March 23, 2023
Need Help Structuring a Python Project with src and test directories
Can anybody explain what is going on? You need to run your code from the folder that contains pitches, as a module, so that the $PYTHONPATH is set correctly for these files to resolve each other. For instance, python -m pitches.test.zones_test, etc. More on reddit.com
🌐 r/learnpython
6
2
April 2, 2021
How to generate unit tests for Python using src and test folder structure (preferable with PyCharm integration)? - Stack Overflow
In my python project I have following folder structure: src foo foo.py baa baa.py test foo baa and would like to generate a unit test file test/foo/test_foo.py that tests src/foo/f... More on stackoverflow.com
🌐 stackoverflow.com
🌐
The Hitchhiker's Guide to Python
docs.python-guide.org β€Ί writing β€Ί structure
Structuring Your Project β€” The Hitchhiker's Guide to Python
README.rst LICENSE setup.py requirements.txt sample/__init__.py sample/core.py sample/helpers.py docs/conf.py docs/index.rst tests/test_basic.py tests/test_advanced.py Β· Let’s get into some specifics. Your module package is the core focus of the repository. It should not be tucked away: ... If your module consists of only a single file, you can place it directly in the root of your repository: ... Your library does not belong in an ambiguous src or python subdirectory.
🌐
Reddit
reddit.com β€Ί r/python β€Ί arguments against separating `test` from `src` in a python package?
r/Python on Reddit: Arguments against separating `test` from `src` in a python package?
March 23, 2023 -

The Python Packaging Authority recommends separating the test directory from the src (source code) directory in a Python application:

https://packaging.python.org/en/latest/tutorials/packaging-projects/#creating-the-package-files

Personally, I have always preferred this approach of keeping tests outside the package rather than mixing them with the source code (tests in package).

However, in the interest of expanding my perspective and learning something new, I am open to exploring alternative viewpoints. What are the main arguments for including tests within the package itself?

Image taken from https://blog.ionelmc.ro/2014/05/25/python-packaging/
🌐
Codesolid
codesolid.com β€Ί how-to-separate-tests-and-source-for-python-tests
How To Separate Source and Tests in Python β€” CodeSolid.com 0.1 documentation
To test this, you want a test_greeter.py file somewhere to write your unit tests for the function, but you don’t want it to live with the source. That’s not something all Python teams do, by the way, but it’s a reasonable choice. So, especially if you come from a Java background, but even if you come from a number of other languages, you probably think you should have a folder structure that looks like this: src/mypackage/greeter.py test/mypackage/test_greeter.py
🌐
pyOpenSci
pyopensci.org β€Ί python-package-guide β€Ί package-structure-code β€Ί python-package-structure.html
Python Package Structure & Layout β€” Python Packaging Guide
The src/package layout is semantically more clear. Code is always found in the src/package directory, tests/ and docs/are in the root directory. ... If your package tests require data, do NOT include that data within your package structure.
Find elsewhere
🌐
Amazon S3
s3.amazonaws.com β€Ί assets.datacamp.com β€Ί production β€Ί course_15974 β€Ί slides β€Ί chapter3.pdf pdf
How to organize a growing set of tests?
Project structure Β· src/ # All application code lives here Β· |-- data/ # Package for data preprocessing Β· | |-- __init__.py Β· | |-- preprocessing_helpers.py # Contains row_to_list(), convert_to_int() |-- features/ # Package for feature generation from preprocessed data Β· |-- __init__.py Β· UNIT TESTING FOR DATA SCIENCE IN PYTHON Β·
🌐
Reddit
reddit.com β€Ί r/learnpython β€Ί need help structuring a python project with src and test directories
r/learnpython on Reddit: Need Help Structuring a Python Project with src and test directories
April 2, 2021 -

I have several questions that relate to Python Packaging and project structure specifically...

I am going through Clean Code in Python and have been trying to implement a better structure in my own project but have been running into some issues separating tests and source files in python and have begun to second guess my overall structure.

My current structure is as follows

β”œβ”€β”€ pitches
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ src
β”‚   β”‚   β”œβ”€β”€ __init__.py
β”‚   β”‚   β”œβ”€β”€ error_dist.py
β”‚   β”‚   β”œβ”€β”€ obvious_zones.py
β”‚   β”‚   β”œβ”€β”€ pitch.py
β”‚   β”‚   β”œβ”€β”€ pitch_zone_enums.py
β”‚   β”‚   β”œβ”€β”€ zone.py
β”‚   β”‚   └── zones.py
β”‚   └── test
β”‚       β”œβ”€β”€ __init__.py
β”‚       β”œβ”€β”€ error_dist_test.py
β”‚       β”œβ”€β”€ obvious_zones_test.py
β”‚       β”œβ”€β”€ pitch_test.py
β”‚       β”œβ”€β”€ test_config.py
β”‚       β”œβ”€β”€ zone_test.py
β”‚       └── zones_test.py

Question 1: In /src I have certain files import other ones - for example, zones.py has from zone import Zone and my linter throws an error saying that it is unable to import zone, but when I run the actual zones.py file there is no error.

When I compare my code to ch02 examples from Clean Code in Python, I notice that no python file in the src folder actually imports sibling python files... (i.e. he does not have zones.py importing an object from zone.py)

Is my structure incorrect or is it fine to import sibling files? If so, why is my python linter indicating an unable to import error but my program runs successfully?

Question 2 I am unable to import files from src in my test folder. E.g. when I try and import obvious_zones in obvious_zones_test.py I do get a linter error AND when I run the obvious_zones_test.py file I get the error ModuleNotFoundError: No module named 'obvious_zone'

TLDR: Am I packaging and structuring my project correctly? How am I supposed to import from src directory into tests folder directory?

I pulled the code from Clean Code in Python onto my local and I actually get an error when I try to run his test individually. However, when I use the make test command everything works... Is anyone able to explain what is happening?

Link to the code I am talking about:

https://github.com/PacktPublishing/Clean-Code-in-Python-Second-Edition/tree/main/book/src/ch02

I can post images but for some reason `make test` works, but trying to run unittests individually fails because of import issues... Can anybody explain what is going on?

Top answer
1 of 1
1

Below is a first draft for a script that generates a unit test skeleton for missing test files. I asked AI to generate that script for me and refactored it a bit.

import inspect
import os


def main():
    src_dir = 'src'
    test_dir = 'test'
    generate_unit_test_skeleton(src_dir, test_dir)


def generate_unit_test_skeleton(src_dir, test_dir):
    # Loop over all folders and files in src directory
    for root, dirs, files in os.walk(src_dir):
        # Get the relative path of the current folder or file
        relative_folder_path = os.path.relpath(root, src_dir)

        # Create the corresponding unit test folder in test directory
        test_folder = generate_test_folder_if_not_exists(relative_folder_path, test_dir)

        # Loop over all files in the current folder
        for file in files:
            # Check if the file has a .py extension
            if file.endswith('.py'):
                generate_unit_tests_for_file(
                    file,
                    relative_folder_path,
                    test_folder,
                )


def generate_unit_tests_for_file(
    file,
    relative_directory,
    test_directory,
):
    # Create the corresponding unit test file in test directory
    generated_test_file_path = generate_unit_test_file(
        file,
        relative_directory,
        test_directory,
    )

    if generated_test_file_path is not None:
        # Get all classes and functions defined in the original file
        classes, functions = determine_members(file, relative_directory)

        # Generate test functions for each class and function
        generate_test_functions(
            file,
            generated_test_file_path,
            classes,
            functions,
        )


def generate_test_functions(
    file,
    test_file_path,
    classes,
    functions,
):
    module_name = determine_module_name(file)

    with open(test_file_path, 'a') as test_file:
        for class_name, class_instance in classes:
            generate_test_function_for_class(
                test_file,
                module_name,
                class_name,
                class_instance,
            )

        for function_name, function_instance in functions:
            generate_test_function_for_function(
                test_file,
                module_name,
                function_name,
                function_instance,
            )


def generate_test_function_for_function(
    test_file,
    module_name,
    function_name,
    function_instance,
):
    # Generate the test function name
    test_function_name = f'test_{function_name}'
    arguments = determine_arguments(function_instance)

    # Write the test function to the test file
    test_file.write(f'def {test_function_name}():\n')
    test_file.write(f'    # TODO: Implement test\n')
    test_file.write(f'    # result = {module_name}.{function_name}({arguments})\n')
    test_file.write(f'    pass\n')
    test_file.write('\n')


def generate_test_function_for_class(
    test_file,
    module_name,
    class_name,
    class_instance,
):
    # Generate the test function name
    test_function_name = f'test_{class_name}'

    arguments = determine_arguments(class_instance)

    # Write the test function to the test file
    test_file.write(f'def {test_function_name}():\n')
    test_file.write(f'    # TODO: Implement test\n')
    test_file.write(f'    # instance = {module_name}.{class_name}({arguments})\n')
    test_file.write(f'    pass\n')
    test_file.write('\n')


def determine_members(file, relative_directory):
    # Get the module name
    module_name = os.path.splitext(file)[0]

    # Import the module
    directory_import_path = relative_directory.replace('\\', '.')
    import_path = f'{directory_import_path}.{module_name}'
    module = __import__(import_path, fromlist=[module_name])

    # Get all classes and functions defined in the module
    classes = inspect.getmembers(module, inspect.isclass)
    functions = inspect.getmembers(module, inspect.isfunction)
    return classes, functions


def determine_arguments(function_instance):
    try:
        signature = inspect.signature(function_instance)
    except ValueError:
        return ''

    parameters = signature.parameters

    arguments = []
    for param in parameters.values():
        argument = determine_argument(param)
        arguments.append(argument)

    argument_string = ', '.join(arguments)
    if len(arguments) > 2:
        argument_string += ','  # leading comma causes line breaks if formatted with black
    return argument_string


def determine_argument(param):
    argument = param.name
    if param.default != inspect.Parameter.empty:
        default_value = determine_default_value(param.default)
        argument += f'={default_value}'
    return argument


def determine_default_value(default_instance):
    if inspect.isfunction(default_instance):
        return default_instance.__name__
    elif isinstance(default_instance, str):
        return f"'{default_instance}'"
    else:
        return default_instance


def generate_unit_test_file(file, relative_directory, test_directory):
    test_file_path = os.path.join(test_directory, f'test_{file}')
    if os.path.exists(test_file_path):
        return None
    else:
        # Open the test file in write mode
        with open(test_file_path, 'w') as f:
            # Write the initial import statement
            import_statement = generate_import_statement(file, relative_directory)
            f.write(import_statement)
            f.write('\n')
    return test_file_path


def generate_import_statement(file, relative_directory):
    directory_import_path = relative_directory.replace('////', '.')
    module_name = determine_module_name(file)
    statement = f'from {directory_import_path} import {module_name}\n'
    return statement


def determine_module_name(file):
    name = os.path.splitext(file)[0]
    return name


def generate_test_folder_if_not_exists(relative_path, test_dir):
    test_folder = os.path.join(test_dir, relative_path)
    if not os.path.exists(test_folder):
        os.makedirs(test_folder)
    return test_folder


if __name__ == '__main__':
    main()

Example result for a file 'test/foo/test_foo.py':

import foo.foo

def test_Language():
    # TODO: Implement test
    # instance = controls.Language(value, names=None, module=None, qualname=None, type=None, start=1, boundary=None,)
    pass

def test_Layout():
    # TODO: Implement test
    # instance = controls.Layout(kwargs)
    pass

def test_SimpleNamespace():
    # TODO: Implement test
    # instance = controls.SimpleNamespace()
    pass
🌐
Real Python
realpython.com β€Ί ref β€Ί best-practices β€Ί project-layout
project layout | Python Best Practices – Real Python
Here, your tests import the installed package instead of the local working directory, avoiding common import errors. But you must install a src/ layout project, typically as an editable install with python -m pip install --editable ., before you can import or run its code.
🌐
Madhudadi
madhudadi.in β€Ί blog home β€Ί posts β€Ί python project best practices. β€Ί essential python project best practices for developers
Python Project Best Practices: Structure & Testing Guide | Madhu Dadi
June 26, 2026 - A good Python project structure includes a README.md, pyproject.toml, .gitignore, .env.example, src/ directory with main modules, tests/, scripts/, docs/, and a .venv/ directory.
🌐
Medium
medium.com β€Ί mlearning-ai β€Ί a-practical-guide-to-python-project-structure-and-packaging-90c7f7a04f95
Guide to Python Project Structure and Packaging | by Joshua Phuong Le | Python in Plain English
June 19, 2024 - Structuring Python projects is very important for proper internal working, as well as for distribution to other users in the form of packages. There are two popular structures: src layout, flat layout.
🌐
Plain English
plainenglish.io β€Ί home β€Ί blog β€Ί python β€Ί guide to python project structure and packaging
Guide to Python Project Structure and Packaging
February 3, 2023 - Structuring Python projects is very important for proper internal working, as well as for distribution to other users in the form of packages. There are two popular structures: src layout, flat layout.
🌐
Cosmicpython
cosmicpython.com β€Ί book β€Ί appendix_project_structure
Appendix B: A Template Project Structure
All the source code for our app, ... expect to grow a folder hierarchy that includes domain_model/, infrastructure/, services/, and api/. Tests live in their own folder....
🌐
Medium
medium.com β€Ί @adityaghadge99 β€Ί python-project-structure-why-the-src-layout-beats-flat-folders-and-how-to-use-my-free-template-808844d16f35
Python Project Structure: Why the β€˜src’ Layout Beats Flat Folders (and How to Use My Free Template) | by Aditya Ghadge | Medium
May 17, 2025 - Here are the four guard-rails baked into the template repo (check them out live in the workflow files and configs at Adityag009/python-project-template): # 1️⃣ Install deps (plus editable package) make install # 2️⃣ Run the full QA gauntlet locally make lint && make test # 3️⃣ Build + start the API with prod-like settings docker-compose up --build Β· With these rails in place, the folder structure isn’t just a suggestion β€” it’s enforced by scripts and bots.
🌐
Python Packaging
packaging.python.org β€Ί en β€Ί latest β€Ί discussions β€Ί src-layout-vs-flat-layout
src layout vs flat layout - Python Packaging User Guide
The flat layout would add the other project files (eg: README.md, tox.ini) and packaging/tooling configuration files (eg: setup.py, noxfile.py) on the import path. This would make certain imports work in editable installations but not regular installations. Due to the firstly mentioned specialty of the src layout, a command-line interface can not be run directly from the source tree, but requires installation of the package in Development Mode for testing purposes.
🌐
pytest
docs.pytest.org β€Ί en β€Ί 7.1.x β€Ί explanation β€Ί goodpractices.html
Good Integration Practices β€” pytest documentation
Within Python modules, pytest also discovers tests using the standard unittest.TestCase subclassing technique. ... Putting tests into an extra directory outside your actual application code might be useful if you have many functional tests or for other reasons want to keep tests separate from actual application code (often a good idea): pyproject.toml src/ mypkg/ __init__.py app.py view.py tests/ test_app.py test_view.py ...
🌐
Real Python
realpython.com β€Ί python-application-layouts
Python Application Layouts: A Reference – Real Python
April 23, 2026 - Test your understanding of Python project structure, from flat and src layouts to organizing packages for CLI, web, and GUI applications.