A Guide the Basics of Python Testing: How to Write Unit Tests and Organize Execution Test Cases

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. 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.

It supports four types of testing, 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.
  • Unit Testing. This is a process that allows testing separate modules of source code – the smallest functional units of software.
  • 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.
  • Performance Testing. This type of testing allows you to evaluate the stability, speed, scalability, and responsiveness of a digital solution under a certain workload.

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 this framework because it is de facto standard Python testing framework and built-in into Python program language libraries.

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 unittest tests. We’ll touch on the concepts of TestCase class, assert methods and look at options for running tests.

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

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
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

How to Set up Unit Testing in Python?

Before you run unit testing in Python, 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, as key advantages in the context of the topic at hand are their built-in support for Python and unit testing. Third-party test Runners, Real-time test reporters, test coverage, CI\CD tools etc.

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 Module

By choosing the unittest framework for unit testing in Python, 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.

Tips for Successful Unit Testing in Python

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.

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.