unittest assertions img.

In this article we will look at the various unittest methods for creating assertions. This will not teach you the basics of unittest like how to create a test class, you can check that in the link below:

In testing we use assertions to verify that the outcomes of a given test are true.  For example, we can use assertion to verify that a value yielded by an operation is equal to a known value. If the outcome is correct, the test is considered to have passed, otherwise it is considered a failure.

The assert statement, is the most basic tools for creating assertions. Consider the following example:

a = 10  
b = 20

assert a + b == 30, 'Test failed'

In the above example, the assertion that 10 + 20 = 30 is correct,  hence nothing is done.

a = 10  
b = 30

assert a + b == 50, 'Test failed'

In the above case, the assertion fails leading to the AssertionError exception being raised.

Basically, unittest assertion methods works as  specialized and functional versions of the assert statement. These methods are inherited by test classes which subclasses the unittest.TestCase class.

For example, the logic in the previous examples can be expressed using the .assertEqual method.

import unittest

class TestAdd(unittest.TestCase):
   def test_integers(self):
      a = 10 
      b = 20 

      self.assertEqual(a + b, 30, msg = 'Test failed')

   def test_lists(self):
      a = [1, 2]
      b = [3, 4]

      self.assertEqual((a + b), [1, 2, 3, 4], msg = 'Test failed')

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

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

OK

The assertion methods

In this section we will discuss some of the assertion methods in unittest. Most of these methods have a negating counterpart, which will pass where it fails, e.g assertEqual and assertNotEqual. We will look at a negating pair together.

Almost all of the methods takes an optional extra argument i.e msg which is the message that will be shown in the exception if the test fails.

assertTrue and assertFalse

When we use the assertTrue() method, we are telling unittest to verify that the result of an expression has a boolean value of True.

assertTrue(exp)

Where exp is the expression to be evaluated.

The following example shows how we can use assertTrue in a test.

import unittest

def is_even(num):
   return num % 2 == 0

class TestExample(unittest.TestCase):
   def test_is_even(self):
      a = 8 
      b = 7 

      self.assertTrue(is_even(a))
      self.assertTrue(is_even(b))

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

F
======================================================================
FAIL: test_integers (__main__.TestAdd.test_integers)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\John\Desktop\foo.py", line 12, in test_integers
    self.assertTrue(is_even(b))
AssertionError: False is not true

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

FAILED (failures=1)

The assertFalse is the counter-method of assertTrue, it verifies that the outcome of an expression has a boolean value of False.

import unittest

def is_even(num):
   return num % 2 == 0

class TestExample(unittest.TestCase):
   def test_is_even(self):
      a = 9 
      b = 7

      self.assertFalse(is_even(a))
      self.assertFalse(is_even(b))

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

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

OK

assertEqual and assertNotequal

 The assertEqual method checks that the two values given as argument are in fact equal. The test passes if they are equal and fails if not.

assertEqual(a, b)

Consider the following example:

import unittest

def add(a, b):
   return a + b

class TestExample(unittest.TestCase):
   def test_add(self):
      self.assertEqual(add(10, 20), 30)
      self.assertEqual(add(-5, 5), 0)

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

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

OK

The assertNotEqual method will verify that two values are not equal. It will pass where assertEqual fails and vice verse.

import unittest

def add(a, b):
   return a + b

class TestExample(unittest.TestCase):
   def test_add(self):
      self.assertEqual(add(10, 20), 0)
      self.assertEqual(add(-5, 5), 0)

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

F
======================================================================
FAIL: test_add (__main__.TestExample.test_add)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\John\Desktop\foo.py", line 9, in test_add
    self.assertEqual(add(10, 20), 0)
AssertionError: 30 != 0

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

FAILED (failures=1)

assertAlmostEqual and assertNotAlmostEqual

Due to how floating point values are represented in memory, testing for equality between them may be troublesome and error prone. Consider the following example:

import unittest

class TestExample(unittest.TestCase):
   def test_foats(self):
      self.assertEqual(((3.0 ** 0.5) ** 2.0), 3.0)

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

F
======================================================================
FAIL: test_foats (__main__.TestExample.test_foats)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\John\Desktop\foo.py", line 6, in test_foats
    self.assertEqual(((3.0 ** 0.5) ** 2.0), 3.0)
AssertionError: 2.9999999999999996 != 3.0

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

FAILED (failures=1)

In the above assertion:

  • The expression to be tested ((3.0 **0.5) ** 2.0),
  • (3.0 ** 0.5)  is the square root of 3.0.
  • By squaring the returned value, we expect to get 3.0 again.
  • The returned value is 2.9999999999999996 which is close enough to be considered 3.
  • Howevere, the test fails because the assertEqual uses the == operator which returns False.

To avoid the issues with floating values, unittest provides the assertAlmostEqual and assertAlmostNotEqual methods. The two methods will allow vagueness in the values up to a certain level of precision

import unittest

class TestExample(unittest.TestCase):
   def test_foats(self):
      self.assertAlmostEqual(((3.0 ** 0.5) ** 2.0), 3.0)

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

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

OK

As shown above, when we use the assertAlmostEqual method, the test passes.

The basic syntax of the two methods is shown below:

assertAlmostEqual(self, a, b, places=None)

assertNotAlmostEqual(self, a, b, places=None)

 Where:

  • a and are the values to be compared.
  • places is the number of decimal places that the values will be rounded to before they are compared. It defaults to 7 decimal places. 

assertRaises

Sometimes we may want to test that a piece of code raises an exception under certain conditions. 

The assertRaises method tests that an expected exception is actually raised. It passes if the exception is raised and fails otherwise. Its syntax is as shown below:

assertRaises(expected_exception, function, *args, **kwargs)

The method runs the function(*args, **kwargs) and checks that it raises the expected_exception.

import unittest

def area(r):
   '''calculates area of a circle'''

   if r < 0:
      raise ValueError("Radius can't be negative.")

   area = 3.14 * r * r  

   return area

class TestExample(unittest.TestCase):
   def test_area(self):
     
      self.assertRaises(ValueError,  area, -7)

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

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

OK

In the above example, we defined a function area() for calculating the area of a circle given its radius. We expect the function to raise a ValueError if the given radius is negative. Since the test passed, we can be certain that the exception was raised.

assertRaises also has a context managers interface, this means that we can use it in a with statement, as in:

with assertRaises(expected_exception):
    do_something()

Consider the following example:

import unittest

def area(r):
   '''calculates area of a circle'''

   if r < 0:
      raise ValueError("Radius can't be negative.")

   area = 3.14 * r * r  

   return area

class TestExample(unittest.TestCase):
   def test_area(self):
     
      with self.assertRaises(ValueError):
         area(-7)

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

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

OK

We can inspect the raised exception by capturing it through the context manager object, as shown below.

import unittest

def area(r):
   '''calculates area of a circle'''

   if r < 0:
      raise ValueError("Radius can't be negative.")

   area = 3.14 * r * r  

   return area

class TestExample(unittest.TestCase):
   def test_area(self):
     
      with self.assertRaises(ValueError) as cm:
         area(-7)

      raised_exception = cm.exception
      print(raised_exception) #display the raised exception

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

Radius can't be negative.
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

The Complete Cheat Sheet

In the previous sections, we have looked at some of the assertion methods, however, there are more that we haven't looked at yet. In the following table we summarize all of the methods and what operation each performs in the background. 

assertTrue(x)

bool(x) is True

assertFalse(x)

bool(x) is False

assertEqual(a, b)

a == b

assertNotEqual(a, b)

a != b

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 isinstnace(a, b)

assertRaises(exc, fun, *args, *kwargs)

fun(*args, **kwargs) raises exc

assertRaisesRegexp(exc, r, fun, *args, **kwargs)

 

assertAlmostEqual(a, b)

round(a-b, 7) == 0

assertNotAlmostEqual(a, b)

round(a-b, 7) != 0

assertGreater(a, b)

a > b

assertGreaterEqual(a, b)

a >= b

assertLess(a, b)

a < b

assertLessEqual(a, b)

a <= b

assertRegexpMatches(s, r)

r.search(s)

assertNotRegexpMatches(a, b)

not r.search(s)

assertItemsEqual(a, b)

sorted(a) == sorted(b)

for unhashable objects

assertDictContainsSubset(a, b)

All key/value pairs in a exist in b

Force tests to fail

When we want a test to fail under some conditions regardless of whether assertions pass or not, we can resort to the fail() method. When this method is called, the test immediately fails.

Consider the following example:

import unittest

class TestExample(unittest.TestCase):
   def test_area(self):
      if 1 < 2:
         self.fail('This test must fail.')
     
if __name__ == '__main__':
   unittest.main()

F
======================================================================
FAIL: test_area (__main__.TestExample.test_area)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\John\Desktop\foo.py", line 7, in test_area
    self.fail('This test must fail.')
AssertionError: This test must fail.

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

FAILED (failures=1)