Writing Unit Tests
Learn to write unit tests for Python functions and classes using the pytest framework.
We'll cover the following
Note: Check the installation guide for pytest before starting this lesson.
The assert
statement
Before we write our first unit test, let’s familiarize ourselves with the assert
statement in Python.
The assert
statement in Python is used for debugging and testing purposes. It allows us to check if a condition is True
, and if it isn’t, it raises an exception called AssertionError
. This can be helpful in writing unit tests to verify if the expected results match the actual results.
assert condition, message
Here, the condition is the expression that we want to check for being True
, and the message is an optional string that we can provide as a custom error message when the assert
statement fails.
If the condition in the assert
statement is True
, the program continues to execute without any issues. However, if the condition is False
, the assert
statement raises an AssertionError
with the optional message as the error message. This allows us to quickly identify and fix issues during testing and debugging.
Python functions
Assuming we already have pytest installed in our environment, the first step is to create a new function to test. Let’s say we have a simple Python function in the addition.py
file that adds two numbers/strings:
def addition(x, y):return x + y
Unit test for the function
To write a unit test for the addition function, follow these steps:
Create a new Python file called
test.py
in the same directory as the original file that contains the addition function.Import the addition function from the original file into the
test.py
file.Write a test function using the pytest framework with a prefix of
test
in its name. This naming convention helps pytest automatically recognize and execute the test function. Pytest requires the test function names to start withtest
. Function names that are not of this format aren’t considered test functions by pytest unless we change the default configurations.Inside the test function, use the
assert
statement to check if the addition function returns the expected result for different input values. Save thetest.py
file.To run the test, navigate to the directory containing the
test_file.py
file in the terminal and execute the following command:
pytest
Pytest automatically discovers the test files, executes all the test functions inside them, and provides an output indicating if the tests passed or failed. This way, we can quickly verify if our addition function is behaving as expected and catch any issues in our code.
When writing tests with pytest, it’s important to follow a naming convention for our test files and functions to ensure that pytest can automatically discover and execute them. The recommended naming convention is to add test_
as a prefix or suffix to test the file name, and test
as a prefix or suffix to the test functions inside the file. We will learn more about test organization and discovery in pytest in upcoming lessons.
If our file name doesn’t follow this convention, we will need to specify the file name in the command:
pytest test.py
from addition import additiondef test_addition_int():assert addition(4, 5) == 9assert addition(12, 2) == 14def test_addition_str():assert addition('a', 'b') == 'ab'
We can either create two different functions—test_addition_int
and test_addition_str
—to test the addition of integer and string, or we can test both in one function.
It’s a good practice to make sure our function names are self-explanatory.
from addition import additiondef test_addition():assert addition(4, 5) == 9assert addition('a', 'b') == 'ab'
The .
next to the file name indicates that the test has passed. If the test fails, pytest shows an error message indicating which assertion failed and what the expected and actual results were.
It’s essential to note that unit tests should cover different scenarios and edge cases. In the above example, we test only a few input values. We should also test for invalid input values and ensure that the function raises the appropriate exception.
Below is an example of a test that fails. Run the test and see the output for yourself.
from addition import additiondef test_addition():result = addition(4, 5)expected = 10assert result == expected, f"Expected {expected}, but got {result}"
Unit test for classes
Just like Python functions, we can test methods of Python classes. Let’s dive into the example directly.
from my_class import MyClass# method 1class TestMyClass1:def test_addition(self):cls_ = MyClass()result = cls_.add(3, 4)assert result == 7def test_subtraction(self):cls_ = MyClass()result = cls_.subtract(7, 3)assert result == 4# method 2class TestMyClass2:def setup_method(self):self.cls_ = MyClass()def test_addition(self):result = self.cls_.add(3, 4)assert result == 7def test_subtraction(self):result = self.cls_.subtract(7, 3)assert result == 4
In this test, we define a class called TestMyClass1
that contains two test methods: test_addition
and test_subtraction
. In each test method, we create an instance of MyClass
, call a method on it with some arguments, and then use an assertion to check if the result is what we expect. In TestClass2
, instead of creating a new instance of MyClass
in each test function, we add setup_method()
.
In test classes, we cannot have the __init__
constructor because pytest creates a new instance of the test class for each test method. Therefore, any setup or initialization code can be placed in the setup_method()
method or in a fixture function. Additionally, defining a constructor can interfere with pytest’s internal mechanisms for test discovery and result reporting.
Note: It is necessary to have
Test
as a prefix for a test class in Python. If you choose to deviate from this convention, you need to change pytest's configuration. We will learn more about this in the Significance of Test Organization lesson.
Conclusion
In conclusion, writing unit tests is an essential part of software development. It helps ensure that the individual units of a software application function correctly and meet the expected requirements. The pytest framework makes it easy to write and run unit tests in Python. By following the above steps, you can write your first unit test and start testing your software application.