Python tutorials > Testing > Unit Testing > How to organize tests?
How to organize tests?
Basic Structure: The Test Directory
tests
. This directory should reside at the top level of your project. Within the tests
directory, you'll mirror the structure of your source code. For example, if you have a module named my_module.py
, you'd create a corresponding test file named test_my_module.py
within the tests
directory.
Mirroring Source Code Structure
my_project/
my_module/
module_a.py
module_b.py
main.py
Your tests
directory should mirror this:tests/
my_module/
test_module_a.py
test_module_b.py
test_main.py
Example: Simple Project Structure
calculator.py
module and a corresponding test_calculator.py
. The TestCalculator
class within test_calculator.py
contains individual test methods for the add
and subtract
functions.
# my_project/calculator.py
def add(x, y):
return x + y
def subtract(x, y):
return x - y
# tests/test_calculator.py
import unittest
from my_project.calculator import add, subtract
class TestCalculator(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
self.assertEqual(add(-1, 1), 0)
def test_subtract(self):
self.assertEqual(subtract(5, 2), 3)
self.assertEqual(subtract(0, 0), 0)
if __name__ == '__main__':
unittest.main()
Using setUp and tearDown
setUp
and tearDown
methods are extremely useful for setting up preconditions before each test and cleaning up afterwards. setUp
is executed before each test method, allowing you to initialize objects or resources. tearDown
is executed after each test method, providing an opportunity to release resources or reset the environment. This ensures that each test runs in isolation. In this example a class MyClass
is instantiated using the setUp
method, thus self.my_instance
is instantiated before each test. The tearDown
method sets to None the instance to clean up resources.
import unittest
class MyClass:
def __init__(self):
self.data = []
def append_item(self, item):
self.data.append(item)
class TestMyClass(unittest.TestCase):
def setUp(self):
# Initialize the MyClass instance before each test
self.my_instance = MyClass()
def tearDown(self):
# Clean up resources after each test (optional)
self.my_instance = None
def test_append_item(self):
self.my_instance.append_item(5)
self.assertEqual(len(self.my_instance.data), 1)
self.assertEqual(self.my_instance.data[0], 5)
def test_another_function(self):
# Another test using self.my_instance
self.my_instance.append_item(10)
self.assertEqual(len(self.my_instance.data), 1)
self.assertEqual(self.my_instance.data[0], 10)
Grouping Tests with Classes
setUp
and tearDown
methods.
Test Suites
unittest
provides its own suite mechanism, tools like pytest
often provide more flexible and powerful ways to manage test execution.
Using a Test Runner (pytest)
pytest
is a popular and powerful test runner for Python. It offers features like auto-discovery of tests, detailed error reporting, and a rich plugin ecosystem. To use pytest
, simply install it (pip install pytest
) and run the pytest
command in your project directory. pytest
automatically discovers test files and functions named test_*.py
and test_*
, respectively.
Concepts Behind the Snippet
Real-Life Use Case Section
tests/
api/
test_user_endpoints.py
test_product_endpoints.py
models/
test_user_model.py
test_product_model.py
utils/
test_email_utils.py
This organization allows you to run tests specific to the API, the data models, or utility functions.
Best Practices
Interview Tip
When to Use Them
Alternatives
Pros
Cons
FAQ
-
What if my project has a flat structure with no modules?
Even in a flat structure, it's still beneficial to create atests
directory. Inside thetests
directory, you can create test files named after the corresponding source files. For example, if you have a file namedmain.py
, you would create atest_main.py
file in thetests
directory. -
How do I handle testing private functions or methods?
Testing private functions or methods directly is generally discouraged, as it can lead to brittle tests that are tightly coupled to the implementation details. Instead, focus on testing the public interface of your classes and modules. If you find yourself needing to test a private function extensively, it might indicate that the function's logic should be moved to a separate, testable module. -
Is it okay to have multiple test classes in a single test file?
Yes, it is acceptable to have multiple test classes in a single test file, especially if the classes are closely related or testing different aspects of the same unit of code. However, keep the file organized and ensure that each class has a clear purpose.