Basics of Python Unit Testing: How to Write, Organize and Execute Tests

Unit testing is an important stage of the software development life cycle, which involves testing small code components to ensure that they work and meet the required quality standards. In this article, we will talk about how to implement unit testing in Python, a popular programming language with a vast ecosystem of libraries and testing frameworks.

Unit tests play a critical role in software testing, as they allow teams to identify bugs in a digital product early in development – even before end users discover them.

Unit testing is considered to be a rather expensive and time-consuming process for developers, but this statement is quite controversial when it comes to Python code testing. Thanks to the variety of flexible Python test frameworks, the challenge is more manageable. After all, this interpreted, object-oriented, high-level programming language offers Devs, SDETs and AQAs several tools that can be used to greatly simplify writing test cases and their execution.

Python Testing Basics: What Types of Testing Are Available?

Python3 is the latest version of the Python programming language, which features improved performance, more consistent syntax, and easier-to-understand code.

Python interface
Resource for Downloading Python packages

Python supports the following five types of testing of its programming code, which differ in the level of detail of the code being tested and the purpose of running tests:

  • Feature Testing. This is a type of QA process aimed at checking whether the functioning of a software product meets the requirements specified in the specification.
  • Python Unit Testing. This process tests a single test of a specific function or class.
  • Python Integration Testing. This stage implies complex testing of several components of a software product that are integrated with each other. This type of testing is usually performed after running unit tests.
  • Python Performance Testing. This type of testing allows you to evaluate the stability, speed, scalability, and responsiveness of a digital solution under a certain workload.
  • Python API Testing. This is a method for verifying the correctness of application interfaces, during which the server’s responses, status codes, the format of transmitted data, and the logic of exchange between the client and server are evaluated. For this, libraries such as requests, unittest, pytest, or httpx are most commonly used.

We made a short list of popular Python unit testing tools. These tools are widely used and discussed in Python communities and are quite effective in ensuring code reliability.

Tool Description Key Features
PyUnit logo Built-in Python testing framework based on Java’s JUnit; supports test discovery, fixtures, and assertions.
  • Test cases, suites, and fixtures for setup/teardown.
  • Object-oriented approach with TestCase class.
  • Automatic test discovery and detailed test reports.
PyTest logo A powerful, easy-to-use testing framework with rich plugin ecosystem and concise syntax.
  • Simple syntax with plain assert statements.
  • Powerful test discovery and rich plugin ecosystem (e.g., pytest-cov, pytest-xdist).
  • Supports unit, functional, and integration testing.
Doctest logo Tests embedded in documentation strings; good for simple, example-based testing.
  • Tests written as interactive Python sessions in docstrings.
  • Simple to use for small projects or documentation-driven testing.
yelp Testify logo A more modern alternative to unittest with additional features and cleaner syntax.
  • Decorator-based fixtures, reducing boilerplate.
  • Supports unit, integration, and system testing.
  • More Pythonic syntax than unittest.
Nose logo unittest extension with additional features and plugins.
  • Automatic test discovery and parameterized testing.
  • Plugin-based system for customization.
  • Supports multiprocessing for faster test execution.

You are free to choose automation Python unit testing based on your project type or testing needs (e.g. web app, project size, API, data science, CI\CD integration capabilities).

In this article, we will cover Python unit testing in detail with the unittest framework and focus on preparing for the QA process, writing tests and their execution.

Why Python unittest framework?

We chose unittest because it’s included in the Python standard library, doesn’t require extra setup, and integrates smoothly with your Python file. It also supports basic test organization using test case class, test file structures, and test module segmentation.

What Is Unittest Framework?

Unittest framework is a tool for unit testing that uses an object-oriented approach and provides users with basic features available in other popular frameworks – for example, JUnit. These include test automation, code sharing, combining tests into collections etc.

More interested in PyTest, then pay attention on these articles 👀

Getting to Know the Unittest Module

In this section, we will talk about how to write the unittest tests. We will touch on the concepts of the TestCase class, assert methods and look at options for running tests. We will walk you through writing your first test, structuring your project, and running test scripts via the command line or even using tools like the command CMD and VS Code.

Please note: as we noted on top ⬆️ this framework is included in the Python standard library, so there is no additional setup required.

The Unittest testing tool supports some important concepts in realizing the key test functions.

🔑 Key Concepts of Unittest
Concept Meaning
Test fixture A list of actions required to prepare the test environment. This is the preparation that precedes the execution of one or a group of tests.
Test case A unit of testing that tests answers for specific datasets. The framework offers the TestCase class, i.e., a base class that allows the creation of test cases and test code.
Test suite A set of test cases for which co-execution is provided.
Test runner A tool responsible for test execution and providing a QA engineer with test results.
📁 Organizing Testing Code With TestCase Class

How to organize code with the TestCase class in Unit Testing Python? It is purely a test Structure in practice. To create your own test cases, define methods inside a test case class that start with the prefix test. Each piece of code inside this function is automatically picked up during the test run. Use assert statement variations to validate the return value against the expected value.

One way to create your own test cases in unittest is to write a subclass of the TestCase class. This subclass implements test methods, whose name starts with test, to execute the required testing code, look at example in code:

import unittest

class DefaultWidgetSizeTestCase(unittesst.TestCas):
    def test_default_widget_size(self):
        widget = Widget ('The widget')
        self.assert.Equal(widget.size(), (50,50))

Note that assert*() methods are used in this framework to perform validation. Here are the most frequently used ones:

assert method* What it checks?
assertEqual(a, b) is it true that a is equal to b – it verifies whether the actual output matches the expected result
assertNotEqual(a, b) is it true that a is NOT equal to b
assertTrue(x) is x a value True
assertFalse(x) is x a value False
assertIs(a, b) is it true that a is the same object as b
assertIn(a, b) is it true that a is present in b
assertNotIn(a, b) is it true that a is missing from b
assertAlmostEqual(a, b, places) is it true that a and b are approximately equal (to the specified number of decimal places)
assertIsNone(x) is it true that x is None
assertIsNotNone(x) is it true that x is NOT None
assertRaises() useful for checking error message handling, like in edge cases (e.g., division by zero).

How to Set up Unit Testing in Python?

Before you run Python Unit testing framework, there are some prerequisites to consider:

  1. Make sure you have an up-to-date version of Python installed on your system.
    python3 --version

    If you need it, you can download the Python source code and installers from the official website. At the time of writing, the latest version is Python 3.12.4.

  2. Choose a testing framework that best suits the needs of your project. There are many Python testing tools available on the market, the most popular are the Pytest, Doctest, PyUnit (unittest) frameworks – we’ll look at the last in detail below in this guide.
  3. Create a clear test project structure. This includes organizing all the tests, modules, and Python code files. This will optimize the testing process.
  4. Check the Python dependencies after installation. This can be done using the pip package management system. Use the pip check command to run the check.
    pip check

    If all dependencies are installed and compatible, the result will be as follows:

    // No broken requirements found.
  5. Create virtual environments. It ensures that the packages and their versions are specific to your project and do not interfere with the packages in other projects and vice versa.
    python -m venv /path/to/new/virtual/environment pip install virtualenv
    venv\Scripts\activate // Activate the virtual environment on Windows.
    source venv/bin/activate // Activate the virtual environment on macOS and Linux.

Select the supporting tools. For example, Visual Studio Code (VS Code), PyCharm editors:

JetBrains website for downloading PyCharm
Modern Python IDE PyCharm

A key advantage in the context of the topic at hand is their built-in support for Python unit testing. Third-party test Runners, Real-time test reporters, test coverage, CI\CD tools, etc.

VisualStudio code Python marketplace
Python PyTest, PyUnit, unittest Extensions

The command palette is a convenient tool that allows you to run editor functions, open files, search for symbols, view document structure, and more – all in one interactive window.

Breakpoints are particularly important for identifying the root cause of failures or unexpected behavior in automated tests. This IDE functionality is essential for Automation QA engineers in test debugging because it allows them to pause test execution at specific points to inspect the current state of the application or the test itself. Similarly, running a particular test from the code editor is possible.

Breakpoint in Visual Studio
Breakpoint IDE in Test Debugging

Once you have selected the tool that works best for you, you will be ready to perform unit testing – testing individual parts of the code to ensure that each one functions properly. Such tests help quickly identify issues in specific components of the program and promptly fix errors.

Write first Python unittest test

Suppose we have a class called Calculator, which contains methods for performing simple arithmetic operations: addition, subtraction, division, and multiplication:

Let’s demonstrate how testing in the unittest framework is performed across our knowledge:

  1. Set Up the Project Directory:
    my_calculator_project/
    │
    ├── calculator.py
    ├── test_calculator.py
    ├── requirements.txt
    └── venv/
  2. Create a Virtual Environment. Navigate to your project directory and create a virtual environment and do not forget to activate it:
    cd my_calculator_project
    python -m venv venv
    
  3. Create a calculator App.
    class Calculator:
        def add(self, a, b):
            return a + b
    
        def subtract(self, a, b):
            return a - b
    
        def multiply(self, a, b):
            return a * b
    
        def divide(self, a, b):
            if b == 0:
                raise ValueError("Cannot divide by zero")
            return a / b
    
  4. Write Unit Tests. Create a file named test_calculator.py for your unit tests.
    import unittest
    from calculator import Calculator
    
    class TestCalculator(unittest.TestCase):
    
        def setUp(self):
            self.calc = Calculator()
    
        def test_add(self):
            self.assertEqual(self.calc.add(1, 2), 3)
            self.assertEqual(self.calc.add(-1, 1), 0)
            self.assertEqual(self.calc.add(-1, -1), -2)
    
        def test_subtract(self):
            self.assertEqual(self.calc.subtract(2, 1), 1)
            self.assertEqual(self.calc.subtract(-1, 1), -2)
            self.assertEqual(self.calc.subtract(-1, -1), 0)
    
        def test_multiply(self):
            self.assertEqual(self.calc.multiply(2, 3), 6)
            self.assertEqual(self.calc.multiply(-1, 1), -1)
            self.assertEqual(self.calc.multiply(-1, -1), 1)
    
        def test_divide(self):
            self.assertEqual(self.calc.divide(6, 3), 2)
            self.assertEqual(self.calc.divide(-1, 1), -1)
            self.assertEqual(self.calc.divide(-1, -1), 1)
            
            with self.assertRaises(ValueError):
                self.calc.divide(1, 0)
    
    if __name__ == '__main__':
        unittest.main()
    
  5. Run the tests. With the virtual environment activated, run the tests using the following command:
    python -m unittest discover
    

    You should see output similar to this:

    ....
    ----------------------------------------------------------------------
    Ran 4 tests in 0.001s
    
    OK
    

Benefits of the Unittest built-in simple Module

Using the Unittest module might streamline your Python automation testing workflow. By choosing the unittest framework for Python unit testing, your team will be able to discover certain positive aspects of this tool:

  • No need for additional installations. Unittest is part of Python’s standard library, allowing you to quickly get started testing without any prior configuration or installation.
  • Clear syntax. The Framework uses an object-oriented approach based on test classes, which is familiar to many developers.
  • Automated test discovery. This speeds up testing because the tool automatically discovers and runs all the tests in a catalog.
  • Built-in test runner. Testers have no need to turn to third-party tools. However, it is possible, if you want to do it. The framework integrates perfectly with other test runners.
  • Extensive functionality. This is achieved by supporting Test fixture, Test case, Test suite, and Test runner concepts.

All of the above benefits will become available to your QA team if you follow some tips for organizing Python unit testing, which we present below.

Interpret Results of Your Python Automated Testing

Interpreting the results of your Python automated tests is crucial to understanding software quality and spotting issues early. Each test outcome—pass, fail, or error—offers insight: passing tests confirm expected behavior, while failures point to broken functionality or mismatched assumptions.

Reviewing tracebacks, grouping results, and prioritization provide context and structure for your testing strategy. Clear test reports or dashboards further aid in communicating across stakeholders.

You can import your Python unit test from your test framework into the test management system testomat.io in this way:

Import Python Unit Test Framework
Import Python Unit Tests into Test Management

By executing them, you can review their outcomes, which are stored in a History Archive for tracking trends, finding bottlenecks. It is a great base for making decisions.

Tips for your Automation Python Testing Framework

Regardless of the tools you use to write unit tests and run them, it is recommended that you follow certain guidelines to get the best results.

Create clear test cases. It is better if they are short enough and easy to understand. To do this, use clear wording for test methods, which will make it obvious what code fragment is being tested. This allows you to understand the purpose of testing accurately.

Write isolated tests. Make sure that each of your tests does not depend on the results and state of other tests. This guarantees their stability due to their independence from external factors.

  • Carefully select the assertions you use. For this purpose, choose an assertion that matches the purpose of the test. It is also advisable to limit yourself to one assertion per test method.
  • When writing tests, consider boundary cases. These may be invalid input data, exceptional values, etc. This will help to detect unexpected behavior.
  • Include unit tests in the CI\CD pipeline. This will automatically run tests in case of changes in source code and ensure consistent execution.
  • Work on improving test coverage. Regularly study reports on test coverage of the code base to identify areas for improvement.
  • Don’t ignore test refactoring. Keep unit tests up-to-date and readable so that they retain their value as the code base grows.
  • Maintain quality test documentation. Add comments and descriptions for complex tests. This will help developers and other members of the QA team understand testing goals and optimize workflows.

Importance CI\CD in Python Automation Testing

CI\CD (Continuous Integration\Continuous Deployment) is important for unit testing because it ensures that tests are automatically and consistently run every time code changes are made. It provides confidence in deployment in a controlled environment. Development teams can safely push changes to production more frequently and receive feedback within minutes if their changes break the code.

The Right Python Unit Testing Framework for Long-Term Success

Python’s unittest is a robust test framework offering seamless test organization, clear syntax, and seamless integration with development tools. With lots of methods available for validation and easy-to-manage test data, it’s a great way to uphold high quality standards in any project. Whether you’re writing your first test case or maintaining complex systems, proper Python unit testing is essential for long-term success.

We hope this guide will help you optimize your Python testing process on your project by using a framework that allows you to create simple and reliable unit tests for your Python codebase.

Still have questions? Contact our expert and get detailed advice on all current trends in modern software testing.