Python unittest Module

Python Unit Test Module

In this article, we’ll cover the Python unittest module and some of its common use cases.

But before that, let’s understand why we need this module in the first place.


Why should you use the unittest Module?

When you’re working with large code-bases, Application development is often categorized into two phases.

  1. Development Phase
  2. Testing Phase

Phase 1 is your development phase, where you build your core idea into a bare-bones application.

But, this is not sufficient if you actually want to use it regularly. There may have been situations that you may have missed, which can actually cause your program to work unexpectedly.

To minimize such errors, there is another phase called Testing Phase, which is aimed at testing different possible scenarios for your application, and check if it’s working correctly.

Often, if you don’t have an established framework for this phase, you may need to verify all scenarios manually, which is tedious.

To reduce the developer’s hassle, we can use the Python unittest module and solve exactly this problem by using automated testing.

Types of testing

For an application, there are two types of tests:

  • Integrated Tests
  • Unit Tests

Integrated Tests are those tests that check if modules of an application work properly alongside each other.

Unit Tests are those which check small components in the application.

While we can write both Integration Tests and Unit Tests, integration tests depend hugely on your application and can combine multiple unit tests.

With all that covered, let’s now look at how we can use this module!


Python unittest Module

This module comes built-in with your Python 3+ installation, so there’s no need to install it using pip.

You can import the module by typing:

import unittest

Python unittest Methods

This module has several methods by which you can perform unittests.

The most common ones are listed in the table below.

MethodAssertion Check
assertEqual(a,b)a == b
assertNotEqual(a,b)a != b
assertTrue(x)bool(x) is True
assertFalse(x)bool(x) is False
assertIs(a,b)a is b
assertIsNot(a, b)a is not b
assertIsNone(x)x is None
assertIsNotNone(x)x is not None
assertIn(a, b)a in b
assertNotIn(a, b)a not in b
assertIsInstance(a, b)isinstance(a, b)
assertNotIsInstance(a, b)not isinstance(a, b)

Writing a Unit Test

We need an program to apply tests on. So let’s write one!

I will write a program that simply tries to verify the sum of elements in a list. We will write a unittest program for that.

Now, to write an individual test case, we need to inherit the unittest.TestCase class, and then override it using some specific methods.

I will call my class MyTestClass.

import unittest

def list_sum(my_list):
    # Sums the elements of the list
    return sum(my_list)

class MyTestClass(unittest.TestCase):
    def test_list(self):
        # Checks if the sum of the below list is as expected
        my_list = [1, 2, 3, 4, 5]
        self.assertEqual(list_sum(my_list), 15, "Should be 15")

    def test_string(self):
        # Checks if the string is 'Hello from AskPython'
        my_str = 'Hi'
        self.assertEqual(my_str, 'Hello from AskPython', "Should be 'Hello from AskPython'")


if __name__ == '__main__':
    # Main module
    unittest.main()

NOTE: To write a test method we must prefix a method name with test_. So, any test method must be of the form test_xyz()

I’m writing a method test_list() that checks if the sum of elements in the list equals 15, and similarly another method to check for the given string.

I’m using unittest‘s assertEqual() method, which will run the unittest and check if this assertion holds.

Let’s now execute this file using Python.

user@AskPython $ python my_test.py 
.F
======================================================================
FAIL: test_string (__main__.MyTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "my_test.py", line 16, in test_string
    self.assertEqual(my_str, 'Hello from AskPython', "Should be 'Hello from AskPython'")
AssertionError: 'Hi' != 'Hello from AskPython'
- Hi
+ Hello from AskPython
 : Should be 'Hello from AskPython'

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

FAILED (failures=1)

As you can see, the first test passed, while the second one failed, since the strings do not match.

You’ve now written your first unittest method!


Run Unit Tests on an Application

Let’s now run unittests on another program, since you won’t be writing your entire application inside a unittest file!

Let’s write a simple application program and perform unit tests on it.

I’ll be writing a program that acts as a very simple database to store the names and marks of students.

Save the below file as test_example.py as we’ll be referencing it in our next piece of code.

class MyClass:
    # Database of {'Name': 'Marks'} dict pairs
    db = dict()
    num_students = 0

    def add_db(self, name, marks):
        self.db[name] = marks
        self.num_students += 1

    def rm_db(self, name):
        # Removes key value pair corresponding
        # to student name
        if name in self.db:
            del self.db[name]
        else:
            return f'Student with Name:{name} not in Database'

    def get_marks(self, name):
        if name in self.db:
            return self.db[name]
        else:
            return f'Student with Name:{name} not in Database'


if __name__ == '__main__':
    my_class = MyClass()
    my_class.add_db('John', 47)
    my_class.add_db('Mary', 34)
    print(my_class.get_marks('John'))

The recommended method to run Unit Tests

It is a common practice to keep the testing modules separated from the core application.

So we will import the unittest module only during the test phase.

Python allows us to do, that, by specifying the -m MODULE_NAME option. So, our command will be:

python -m unittest -v my_test.py

We’ll use the -v verbose option to display all helpful messages.

Now, you don’t need to write import unittest on your application!

To run unit tests, we must write a test file for our program, similar to the one we did before. We’ll also import the MyClass that we created earlier by referencing the file test_example.py that we saved earlier.

import unittest

from test_example import MyClass

import random

class MyTest(unittest.TestCase):
    # Init the MyClass class
    my_class = MyClass()

    # Database to verify test cases
    database = dict()

    def test_case_1(self):
        print("\n\nRunning Test 1....\n\n")

        name = 'John Doe'
        marks = 50
        self.database[name] = marks
        self.my_class.add_db(name, marks)
        self.assertEqual(self.database, self.my_class.db)
        print(self.database)
        print("\n\nFinished Test 1\n\n")

    def test_case_2(self):
        print("\n\nRunning Test 2....\n\n")
        for i in range(5):
            name = ''
            for j in range(6):
                name += chr(random.randint(97, 97+25))
            marks = random.randint(0, 100)
            self.database[name] = marks

            # Insert to MyClass Database
            self.my_class.add_db(name, marks)
        # Now check if both databases have the same key:value pairs
        self.assertEqual(self.database, self.my_class.db)
        print(self.database)
        print("\n\nFinished Test 2\n\n")


if __name__ == '__main__':
    # Run the main unittest code
    unittest.main()

Now that we’ve written the tests separately, let’s verify if it works.

python -m unittest run_tests.py
Unittest Example
Unittest Example

This does work, since both our tests passed!

Note that our final test database contains records from both Test1 and Test2, so it’s possible to manipulate the test mechanism based on your program!


Conclusion

Hopefully, you now understand how you can use Python’s unittest module to perform sanity checks during the testing phase. If you have any queries, do mention them in the comment section below!


References