Python Assert Exception

Neema Muganga Oct 10, 2023
  1. Using Context Manager to Catch Python Assert Exception
  2. Using Keyword Arguments to Catch Python Assert Exception
Python Assert Exception

This article brings one to an understanding of assert as a unit of test, to test that the functions can throw exceptions(errors detected during code execution) without necessarily exiting the execution. In other words, the thrown exception is encapsulated.

This test will pass if an exception is raised. An error is thrown if a different exception other than the one expected is thrown. In an instance where there is no raised exception at all, the test fails.

Using Context Manager to Catch Python Assert Exception

Similar to how it allows the allocation and release of resources when necessarily required in the general python concept, the context here gets hold of the actual exception object being thrown during the test.

It stores this exception attribute in the object if there is a need for an additional performance check on the raised exception.

To test that a function fails or passes whether an exception is thrown or not, we will employ TestCase.assertRaises from the unittest module.

Let’s see a practical example using the context manager.

import unittest


class TestCase(unittest.TestCase):
    def test_nameerror(self):
        with self.assertRaises(Exception):
            100 * (someNumber / 5)


if __name__ == "__main__":
    unittest.main()

Output:

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

In the example code,the test passes because we expect an exception to be thrown. We are dividing 5 by an undefined variable someNumber. Therefore, the function throws a NameError exception. Hence, our test passes as displayed by the . in the first line of the output.

Let’s see an example where the test fails when an exception is not thrown.

import unittest


class TestCase(unittest.TestCase):
    def test_nameerror(self):
        with self.assertRaises(Exception):
            someNumber = 10
            100 * (someNumber / 5)


if __name__ == "__main__":
    unittest.main()

Output:

F
======================================================================
FAIL: test_nameerror (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):  File "c:\Users\Neema\Desktop\Article Requirement Spec and Example 
Articles\Article Requirement Spec 
and Example Articles\fah.py", line 106, in test_nameerror
    100 * (someNumber/5)
AssertionError: Exception not raised

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

In this example code, we define a value for someNumber then we perform the arithmetic expression with it.

The output shows us that the test fails as expected. We do not expect any NameError exception to be thrown this time since a defined value for someNumber does exist.

We can as well implement the context manager using user-defined exceptions like in the following example.

import unittest


def user_function():
    raise Exception("A user-defined exception")


class MyTestCase(unittest.TestCase):
    def test_userexception(self):
        with self.assertRaises(Exception) as context:
            user_function()

        self.assertTrue("A user-defined exception" in str(context.exception))


if __name__ == "__main__":
    unittest.main()

Output:

.
----------------------------------------------------------------------
Ran 1 test in 0.006s

OK

Remember to use str to enclose the context.exception to avoid getting a TypeError.

We introduce assertTrue, a unittest library that compares a test value with true in unit testing.

You can also opt to use assertIn instead of assertTrue.

Example:

import unittest


def user_function():
    raise Exception("A use defined exception")


class MyTestCase(unittest.TestCase):
    def test_userexception(self):
        with self.assertRaises(Exception) as context:
            user_function()

        self.assertIn("A use defined exception", str(context.exception))


if __name__ == "__main__":
    unittest.main()

We see the same output where the test passes will be produced.

Using Keyword Arguments to Catch Python Assert Exception

Unlike with the context manager, where we only pass the exception to assertRaises(), we also pass the function call and the function’s parameters as keyword arguments to evoke the exception.

Syntax

assertRaises(exception, function, *args, **keywords)

Example:

import unittest


class MyTestCase(unittest.TestCase):
    def test_division_by_error(self):
        import operator

        self.assertRaises(ZeroDivisionError, operator.floordiv, 55, 0)


if __name__ == "__main__":
    unittest.main()

Output:

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

The example code above used assertRaises() with keyword arguments. We passed to it the ZeroDivisionError exception expected after trying to divide a number with zero. We imported the operator function used with the floordiv operator function as the second argument. Parameter values here are the third parameters - 55 to be divided by 0.

The last application of assert is when we pass in a different exception other than the expected one to assertRaises(). An error is produced instead.

Using the same example code we have just used above, let’s implement this.

import unittest


class MyTestCase(unittest.TestCase):
    def test_division_by_error(self):
        import operator

        self.assertRaises(TypeError, operator.floordiv, 55, 0)


if __name__ == "__main__":
    unittest.main()

Output:

E
======================================================================
ERROR: test_division_by_error (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):  File "c:\Users\Neema\Desktop\Article Requirement Spec and Example 
Articles\user.py", line 16, in test_division_by_error
    self.assertRaises(TypeError, operator.floordiv, 55, 0)
  File "C:\Users\Neema\AppData\Local\Programs\Python\Python39\lib\unittest\case.py", line 733, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "C:\Users\Neema\AppData\Local\Programs\Python\Python39\lib\unittest\case.py", line 201, in handle
    callable_obj(*args, **kwargs) 
ZeroDivisionError: integer division or modulo by zero

----------------------------------------------------------------------
Ran 1 test in 0.031s

FAILED (errors=1)

From the output, an error traceback is produced. We expect a ZeroDivisionError exception when we divide a number by zero. Instead, we pass in a TypeError exception applied when performing an arithmetic operation between different data types, say a string and an integer.

Related Article - Python Exception