Getting Started With Unit Testing in Python
Unit testing is an essential practice in Python software development. It involves testing small, isolated pieces of code, called “units,” to ensure they function correctly. By catching errors early, unit testing enhances code quality, reduces bugs in production and makes programs more reliable and maintainable.
Key points:
- Each test focuses on a specific part of the code to verify its correctness.
- Errors are identified early, preventing issues from spreading.
- Unit tests improve overall code reliability and simplify future maintenance.
Unit Testing Frameworks
Python has several frameworks to help write and run unit tests efficiently:
- unittest: Built into Python’s standard library.
- pytest: Simplified syntax and powerful features.
- nose: Extends unittest for easier testing.
- doctest: Tests examples written in docstrings.
Before using frameworks, it’s important to understand assert statement.
assert Statement
The assert statement is used to verify that a condition holds true in your code. If the condition is false, it raises an AssertionError, helping catch bugs early by validating assumptions during development.
Syntax
assert condition, message
- condition: Expression to check. If True, execution continues. If False, raises AssertionError.
- message (optional): Displayed if the assertion fails, providing context.
Example
x = 5
assert x > 0, "x must be positive"
Explanation: x > 0 is the condition while, "x must be positive" is the message.
Implementation of Unit Test
1. Using unittest
unittest is Python’s built-in framework for writing and running tests. It helps you verify that your code behaves as expected by organizing tests into reusable test cases. To use it, start by importing library:
import unittest
Example: This example shows how to create a unit test using the unittest framework. It defines a test class with a method that checks correctness of a calculation.
import unittest
class TestSum(unittest.TestCase):
def test_sum_numbers(self):
data = [100, 200, 300]
result = sum(data)
self.assertEqual(result, 600)
if __name__ == '__main__':
unittest.main()
Output
----------------------------------------------------------------------
Ran 1 test in 0....
Explanation:
- TestSum inherits from unittest.TestCase, giving access to testing methods.
- test_sum_numbers calculates sum of data and checks it using self.assertEqual(result, 600).
- If the assertion passes, test succeed if it fails, unittest shows an error with expected vs. actual values.
2. Using Pytest
pytest is a Python testing framework that makes writing and running tests simpler and more flexible. It can run existing unittest test cases while offering additional benefits such as:
- Using Python’s built-in assert statements for readability.
- Filtering or selecting specific test cases to run.
- Continuing from the last failing test without restarting the entire suite.
To install pytest, run following command in your terminal or command prompt:
pip install pytest
Example: Let's start with a simple example. Here, we will make a file named as math_functions.py containing functions to add and subtract two numbers.
def add(x, y):
return x + y
def subtract(x, y):
return x - y
Now, we will make another file named as test_math_functions.py. We are making this file to perform testing using pytest using sample inputs.
# test_math_functions.py
import math_functions
def test_add():
assert math_functions.add(2, 3) == 5
assert math_functions.add(-1, 1) == 0
assert math_functions.add(0, 0) == 0
def test_subtract():
assert math_functions.subtract(5, 3) == 2
assert math_functions.subtract(0, 0) == 0
assert math_functions.subtract(-1, -1) == 0
To run these tests using pytest, navigate to directory containing these files in your terminal and run:
pytest
Output

Explanation:
- math_functions.py has functions to test and test_math_functions.py has test cases.
- Pytest automatically finds functions starting with test_ in files named test_*.py.
- assert checks if output matches expected result, failures are reported by pytest.
- Run tests by navigating to folder and executing pytest in terminal.
- Pytest can also run unittest cases, but here we use plain pytest-style assertions.
3. Using Nose
Nose is a testing framework test runner that extends capabilities of Python's built-in unittest module. It provides a simpler syntax for writing tests and offers additional features for test discovery, parallel test execution and plugin support.
To install Nose use below command in command prompt:
pip install nose
Example: Let’s see how to perform simple unit tests using Nose. We will create a file named main.py with test functions to check basic addition and subtraction.
def test_addition():
assert 1 + 1 == 2
def test_subtraction():
assert 3 - 1 == 2
To run this program, we will write following command:
nosetests main.py
Output

Explanation:
- Nose automatically detects functions whose names start with test_.
- It allows writing simple test functions with plain assert statements, avoiding boilerplate required by unittest.
4. Using Doctest
Doctest Module is used to write tests in form of interactive Python sessions within docstrings and then automatically run those tests to verify that code behaves as expected. Doctest Module finds patterns in docstring that looks like interactive shell commands.
Example: Let’s create a file named main.py and write doctest-based tests directly in the function docstrings. These examples will automatically check if the functions work correctly.
def add(a, b):
"""
Function to add two numbers.
>>> add(2, 3)
5
>>> add(-1, 1)
0
"""
return a + b
def subtract(a, b):
"""
Function to subtract two numbers.
>>> subtract(5, 2)
3
>>> subtract(10, 5)
4
"""
return a - b
To run the program, write following command in terminal in same directory as main.py file.
python -m doctest main.py
Output

Explanation:
- Tests are written as examples in the function docstrings using >>>.
- doctest checks if the output matches the expected results.
Writing Assertions in Python
unittest module provides many assertion methods to check values, types and existence of variables. Below are some of the methods that are commonly used to write assertions.
Method | Description |
|---|---|
.assertEqual(a, b) | Checks if a == b |
.assertTrue(x) | Checks if x is True |
.assertIsInstance(a, b) | Checks if a is an instance of class b |
.assertIsNone(x) | Checks if x is None |
.assertFalse(x) | Checks if x is False |
.assertIs(a, b) | Checks if a is b |
.assertIn(a, b) | Checks if a is a member of b |
.assertRaises(Exception) | Checks if a block of code raises an exception |
Advanced Testing Scenarios
In real-world projects, you often need to test more than just basic cases. Advanced testing techniques help ensure your code works correctly in different situations. Key concepts include:
Fixtures
- Fixtures are setup data or resources used across multiple tests.
- They help avoid repeating code by creating input data once and reusing it in several tests.
Parameterization
- Parameterization allows running the same test multiple times with different inputs.
- This ensures the function behaves correctly for a variety of input values without writing separate test functions.
Handling Different Scenarios
- Tests should cover normal cases, edge cases and expected errors.
- You may also write integration tests to check how multiple units work together.
- Testing in multiple environments ensures your code works consistently across setups.
Example: This demonstrates how to write unit tests using unittest, including handling bad data types. We test normal numeric inputs as well as cases where an invalid type should raise a TypeError.
import unittest
class TestSumDifferentPatterns(unittest.TestCase):
def test_list_tuple_values(self):
data = (10*2, 200*2)
result = sum(data)
self.assertEqual(result, 600)
def test_bad_data_type(self):
data = "alpha value passed instead of numeric"
with self.assertRaises(TypeError):
result = sum(data)
if __name__ == '__main__':
unittest.main()
Output
----------------------------------------------------------------------
Ran 2 tests in 0.001s
Explanation:
- test_list_and_tuple_values: Verifies that sum works for normal numeric inputs.
- test_invalid_data_type: Uses self.assertRaises(TypeError) to check that an error is raised when invalid input is provided.
- Using with self.assertRaises() ensures your test detects expected errors, making your code more robust.