Python
unit testing
test directory structure
software development
testing best practices

Where do the Python unit tests go?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Python projects, unit tests usually go in a top-level tests/ directory that mirrors the structure of the application code. That is not the only valid layout, but it is the most common and easiest pattern for teams, tools, and CI systems to understand.

The Common Project Layout

A typical project looks like this:

text
1myproject/
2  pyproject.toml
3  src/
4    myapp/
5      __init__.py
6      math_utils.py
7      api.py
8  tests/
9    test_math_utils.py
10    test_api.py

This structure keeps production code and test code separate. It also makes it obvious which files are shipped as part of the application and which exist only for verification.

Mirroring the Package Structure

As the project grows, it helps to mirror the source tree inside tests/.

text
1myproject/
2  src/
3    myapp/
4      services/
5        billing.py
6      models/
7        user.py
8  tests/
9    services/
10      test_billing.py
11    models/
12      test_user.py

This makes navigation easier because a test for src/myapp/services/billing.py is predictably located near tests/services/test_billing.py.

The top-level tests/ layout has a few practical advantages:

  • test code does not get mixed with runtime package files
  • packaging rules stay simpler
  • test discovery tools such as pytest work naturally
  • large teams can scan the project structure quickly

With pytest, the common naming convention is:

  • directories named tests
  • files named test_*.py
  • test functions named test_*

Example:

python
1from myapp.math_utils import add
2
3
4def test_add_two_numbers():
5    assert add(2, 3) == 5

In-Package Tests Are Also Possible

Some projects keep tests near the code they verify:

text
1myproject/
2  myapp/
3    math_utils.py
4    test_math_utils.py

This can work, especially in small libraries, but it mixes production and test files. That tradeoff is sometimes acceptable, but many teams prefer the cleaner separation of a dedicated tests/ tree.

A Practical pytest Setup

With a pyproject.toml, you can make test discovery explicit:

toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]

Now pytest knows exactly where to look:

bash
pytest

This is especially helpful once the repository contains scripts, notebooks, or examples that should not be treated as tests.

Keep Test Names Close to User Behavior

Directory placement answers where tests go physically, but naming answers whether they stay readable later. A useful pattern is to let the file mirror the module while the test functions describe behavior:

python
1from myapp.api import format_user
2
3
4def test_format_user_includes_display_name():
5    assert format_user({"name": "Ada"}) == "Ada"

That combination gives you both kinds of clarity:

  • file location tells you what module is under test
  • function name tells you what behavior is expected

Common Pitfalls

The most common mistake is scattering tests randomly across the repository with no naming convention. Test tools can be configured to handle that, but the project becomes harder to navigate.

Another issue is importing application code incorrectly when using a src/ layout. If your package lives under src/, your environment or tool configuration must install or expose that package properly for tests.

A third pitfall is naming tests after implementation details that change frequently. It is better for the directory layout to mirror modules, but for the test cases themselves to reflect behavior.

Finally, do not confuse unit tests with integration tests or end-to-end tests. They can live under the same top-level tests/ directory, but keeping subdirectories or markers for different test types usually helps.

Summary

  • The most common place for Python unit tests is a top-level tests/ directory.
  • Mirror the structure of the source tree as the project grows.
  • Use standard file naming such as test_*.py for easy discovery.
  • In-package tests are possible, but many teams prefer a separate test tree.
  • Keep the layout predictable so humans and tools can find tests quickly.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.