Skip to main content
HomeAbout PythonLearn Python

Unit Testing in Python Tutorial

Learn what unit testing is, why its important, and how you can implement it with the help of Python.
Updated Dec 2022  · 10 min read
Python Unit Testing
Source

What is Unit Testing?

Unit testing is a software testing method by which individual units of source code are put under various tests to determine whether they are fit for use. It determines and ascertains the quality of your code.

Generally, when the development process is complete, the developer codes criteria, or the results that are known to be potentially practical and useful, into the test script to verify a particular unit's correctness. During test case execution, various frameworks log tests that fail any criterion and report them in a summary.

The developers are expected to write automated test scripts, which ensures that each and every section or a unit meets its design and behaves as expected.

Python Unit Testing

Though writing manual tests for your code is definitely a tedious and time-consuming task, Python's built-in unit testing framework has made life a lot easier.

The unit test framework in Python is called unittest which comes packaged with Python.

Unit testing makes your code future-proof since you anticipate the cases where your code could potentially fail or produce a bug. Though you cannot predict all of the cases, you still address most of them.

A unit could be bucketed into various categories:

  • An entire module,
  • An individual function,
  • A complete interface like a class or a method.

The best ways to write unit tests for your code is to first start with the smallest testable unit your code could possibly have, then move on to other units and see how that smallest unit interacts with other units, this way you could build up a comprehensive unit test for your applications.

Python's unit testing framework was inspired by java's JUnit and has similar features as major unit testing frameworks in other languages. Python's unit testing framework offers various features, including:

  • Test automation
  • Sharing of setup and shutdown code for tests
  • Aggregating tests into collections
  • Independence of the tests from the reporting framework

Now let's take an example and understand why you need to have unit testing of your code.

Getting Started with Python unittest

Below, we've outlined the steps you need to use Python's unittest framework

Creating a cube function

Let's write a code to calculate the volume of a cube in Python

def cuboid_volume(l):
    return (l*l*l)

length = [2,1.1, -2.5, 2j, 'two']

for i in range(len(length)):
    print ("The volume of cuboid:",cuboid_volume(length[i]))

The volume of cuboid: 8
The volume of cuboid: 1.3310000000000004
The volume of cuboid: -15.625
The volume of cuboid: (-0-8j)



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-7-67e1c37a435f> in <module>
      1 for i in range(len(length)):
----> 2     print ("The volume of cuboid:",cuboid_volume(length[i]))


<ipython-input-2-f50464fd88da> in cuboid_volume(l)
      1 def cuboid_volume(l):
----> 2     return (l*l*l)


TypeError: can't multiply sequence by non-int of type 'str'

Result analysis

The above output should give you some intuition about the importance of having a unit test in place for your code. Now there are three things that are certainly incorrect in the above code:

  • First, the volume of cuboid being negative,
  • Second, the volume of the cuboid is a complex number,
  • Finally, the code resulting in a TypeError since you cannot multiply a string, which is a non-int.

The third problem thankfully resulted in an error while the first & second still succeeded even though the volume of the cuboid cannot be negative and a complex number.

Unit tests are usually written as a separate code in a different file, and there could be different naming conventions that you could follow. You could either write the name of the unit test file as the name of the code/unit + test separated by an underscore or test + name of the code/unit separated by an underscore.

For example, let's say the above code file name is cuboid_volume.py, then your unit test code name could be cuboid_volume_test.py

Without any further ado, let's write the unit test for the above code.

Using assertAlmostEqual

First, let's create a python file with the name volume_cuboid.py, which will have the code for calculating the volume and second with the name test_volume_cuboid.py, which will have the unit testing code.

import unittest

The TestCuboid class inherits the unittest module, and in this class, you would define various methods that you would want your unit test should check with your function cuboid_volume.

The first function you will define is test_volume, which will check whether the output your cuboid_volume gives is equal to what you expect. To achieve this, you will make use of the assertAlmostEqual method.

Test 1

class TestCuboid(unittest.TestCase):
    def test_volume(self):
        self.assertAlmostEqual(cuboid_volume(2),8)
        self.assertAlmostEqual(cuboid_volume(1),1)
        self.assertAlmostEqual(cuboid_volume(0),0)
        self.assertAlmostEqual(cuboid_volume(5.5),166.375)
code 1
code 2

Let's run the above script. You would run the unittest module as a script by specifying -m while running it.

!python -m unittest test_volume_cuboid.py


Output:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Great! So you got your first unit test code working.

The test ran successfully and returned, OK, which means the cuboid_volume function works as you would expect it too.

Test 2

Let's see what happens when one of the assertAlmostEqual methods fails.

code 3

Notice that the last assert statement has been modified.

!python -m unittest test_volume_cuboid.py


Output:

F
======================================================================
FAIL: test_volume (test_volume_cuboid.TestCuboid)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\hda3kor\Documents\Unit_Testing_Python\test_volume_cuboid.py", line 15, in test_volume
    self.assertAlmostEqual(cuboid_volume(5.5),0)
AssertionError: 166.375 != 0 within 7 places (166.375 difference)

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

Well, from the above output, you can observe that the last assert statement resulted in an AssertionError, hence a unit test failure. Python's unit test module shows you the reason for the failure, along with the number of failures your code has.

Using assertRaises

Now let's explore another assert method, i.e., assertRaises, which will help you in finding out whether your function cuboid_volume handles the input values correctly.

Let's say you want to test whether your function cuboid_volume handles the class or type of input, for example, if you pass a string as an input, will it handle that input either as an exception or with an if condition since the length of the cuboid can never be a string.

code 4
!python -m unittest test_volume_cuboid.py


Output:

F.
======================================================================
FAIL: test_input_value (test_volume_cuboid.TestCuboid)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\hda3kor\Documents\Unit_Testing_Python\test_volume_cuboid.py", line 17, in test_input_value
    self.assertRaises(TypeError, cuboid_volume, True)
AssertionError: TypeError not raised by cuboid_volume

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)

Great! So from the above output, it is evident that your code volume_cuboid.py doesn't take care of the input being passed to it properly.

Modifying the cube function

Let's add a condition in the volume_cuboid.py to check whether the input or length of the cuboid is a boolean or a string and raise an error.

code 5
!python -m unittest test_volume_cuboid.py


Output:

..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

Running the Python Testing Script in Jupyter lab

Let's now run the testing script within the jupyter lab. To achieve this, you would first define the testing script in the jupyter lab, as shown below.

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 25 20:16:58 2020

@author: Aditya
"""
from volume_cuboid import *
import unittest

class TestCuboid(unittest.TestCase):
    def test_volume(self):
        self.assertAlmostEqual(cuboid_volume(2),8)
        self.assertAlmostEqual(cuboid_volume(1),1)
        self.assertAlmostEqual(cuboid_volume(0),1)

    def test_input_value(self):
        self.assertRaises(TypeError, cuboid_volume, True)

Then, you will use the unittest.main() method to run the testing script, you can pass several arguments to the below method, out of which one is verbosity level.

Let's experiment with different verbosity levels and see how it changes the output description.

Verbosity 0

unittest.main(argv=[''],verbosity=0, exit=False)

Output:

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
<unittest.main.TestProgram at 0x1de02774348>

Verbosity 1

unittest.main(argv=[''],verbosity=1, exit=False)

Output:

..
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

<unittest.main.TestProgram at 0x1de027a1cc8>

Verbosity 2

unittest.main(argv=[''],verbosity=2, exit=False)

Output:

test_input_value (__main__.TestCuboid) ... ok
test_volume (__main__.TestCuboid) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

<unittest.main.TestProgram at 0x1de027a8308>

Visualizing the AssertionError

Lastly, let's modify the final assertAlmostEqual in the test_volume method and analyze how the verbosity level 2 will show the method failure.

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 25 20:16:58 2020

@author: Aditya
"""
from volume_cuboid import *
import unittest

class TestCuboid(unittest.TestCase):
    def test_volume(self):
        self.assertAlmostEqual(cuboid_volume(2),8)
        self.assertAlmostEqual(cuboid_volume(1),1)
        self.assertAlmostEqual(cuboid_volume(0),1)

    def test_input_value(self):
        self.assertRaises(TypeError, cuboid_volume, True)
unittest.main(argv=[''],verbosity=2, exit=False)


Output:

test_input_value (__main__.TestCuboid) ... ok
test_volume (__main__.TestCuboid) ... FAIL

======================================================================
FAIL: test_volume (__main__.TestCuboid)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython-input-28-46ad33b909ee>", line 14, in test_volume
    self.assertAlmostEqual(cuboid_volume(0),1)
AssertionError: 0 != 1 within 7 places (1 difference)

----------------------------------------------------------------------
Ran 2 tests in 0.003s

FAILED (failures=1)





<unittest.main.TestProgram at 0x1de027b5448>

Summary

From the above outputs, the following conclusions can be made:

  • Verbosity level 0 just shows how many tests were run, i.e., 2 tests along with the time it took to run them,
  • Verbosity level 1 add two dots .. which signifies two tests were executed,
  • Verbosity level 2 shows the detailed method names which were run along with their state ok or FAIL.

Available unittest Assert Methods

There are a lot of assert methods in the unit test module of Python, which could be leveraged for your testing purposes.

Commonly used assert methods

Commonly used assert methods

Source : unittest documentation

Production checks

By using the following methods, you can check production of exceptions, warnings, and log messages.

Production checks

Source : unittest documentation

Task-specific methods

Task specific methods

Source : unittest documentation

To know in detail about the assert methods, check out Python's official documentation.

You could also learn about them with the help of the Pydoc module, which is similar to a help function in Python. A demonstration of how you could use the Pydoc module to read about the assertCountEqual method's documentation is shown below.

!python -m pydoc unittest.TestCase.assertCountEqual

Output:

Help on function assertCountEqual in unittest.TestCase:

unittest.TestCase.assertCountEqual = assertCountEqual(self, first, second, msg=None)

    An unordered sequence comparison asserting that the same elements,

    regardless of order.  If the same element occurs more than once,

    it verifies that the elements occur the same number of times.

        self.assertEqual(Counter(list(first)),

                         Counter(list(second)))

     Example:

        - [0, 1, 1] and [1, 0, 1] compare equal.

        - [0, 0, 1] and [0, 1] compare unequal.

Conclusion

This tutorial was a basic introduction to unit testing in python and its importance as a developer. One good exercise for you would be to write a unit test module for any of your past projects. This will give you a good hands-on experience with writing unit tests. Also, try exploring the remaining Assert methods. You can read more about unit testing with our Pytest tutorial

If you are just getting started in Python and would like to learn more, take our Introduction to Data Science in Python course.

Topics

Python Courses

Course

Introduction to Data Science in Python

4 hr
453K
Dive into data science using Python and learn how to effectively analyze and visualize your data. No coding experience or skills needed.
See DetailsRight Arrow
Start Course
See MoreRight Arrow
Related

Mastering the Pandas .explode() Method: A Comprehensive Guide

Learn all you need to know about the pandas .explode() method, covering single and multiple columns, handling nested data, and common pitfalls with practical Python code examples.
Adel Nehme's photo

Adel Nehme

5 min

Python NaN: 4 Ways to Check for Missing Values in Python

Explore 4 ways to detect NaN values in Python, using NumPy and Pandas. Learn key differences between NaN and None to clean and analyze data efficiently.
Adel Nehme's photo

Adel Nehme

5 min

Seaborn Heatmaps: A Guide to Data Visualization

Learn how to create eye-catching Seaborn heatmaps
Joleen Bothma's photo

Joleen Bothma

9 min

Test-Driven Development in Python: A Beginner's Guide

Dive into test-driven development (TDD) with our comprehensive Python tutorial. Learn how to write robust tests before coding with practical examples.
Amina Edmunds's photo

Amina Edmunds

7 min

Exponents in Python: A Comprehensive Guide for Beginners

Master exponents in Python using various methods, from built-in functions to powerful libraries like NumPy, and leverage them in real-world scenarios to gain a deeper understanding.
Satyam Tripathi's photo

Satyam Tripathi

9 min

Python Linked Lists: Tutorial With Examples

Learn everything you need to know about linked lists: when to use them, their types, and implementation in Python.
Natassha Selvaraj's photo

Natassha Selvaraj

9 min

See MoreSee More