# Python subtests

Today I learned what Python subtests are. The Distinguishing test iterations using subtests (opens new window) section in the unittest docs explain it very well.

It is similar to Pytest's @pytest.mark.parametrize (opens new window) because it also allows you to test different sets of arguments using a single test function.

It is best illustarted with an example:














 


import unittest


class PowerTest(unittest.TestCase):
    def test_power(self):
        parameters = [
            # (base, power, expected_result)
            (1, 1, 1),
            (1, 2, 1),
            (2, 2, 4),
            (3, 2, 9),
        ]
        for p in parameters:
            with self.subTest(base=p[0], power=p[1]):
                self.assertEqual(p[0] ** p[1], p[2])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

We can pass any arguments to self.subTest that will give any failed tests more context. For example, if we add two more parameters (3, 1, 4), (4, 2, 4) and run the test, the output will be:

======================================================================
FAIL: test_power (test.PowerTest) (base=3, power=1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 21, in test_power
    self.assertEqual(p[0] ** p[1], p[2])
AssertionError: 3 != 4

======================================================================
FAIL: test_power (test.PowerTest) (base=4, power=2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 21, in test_power
    self.assertEqual(p[0] ** p[1], p[2])
AssertionError: 16 != 4

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

FAILED (failures=2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

We see above that instead of stopping further execution of the failing test method, Python continued to run other subtests in the function.

When we remove the self.subTest context manager, the output will be:

F
======================================================================
FAIL: test_power (test.PowerTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 20, in test_power
    self.assertEqual(p[0] ** p[1], p[2])
AssertionError: 3 != 4

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

FAILED (failures=1)
1
2
3
4
5
6
7
8
9
10
11
12
13

This gives us much less context about the test and Python didn't run the rest of the parameters after getting one failure.

Newsletter

If you'd like to subscribe to my blog, please enter your details below. You can unsubscribe at any time.

Powered by Buttondown.

Last Updated: 11/20/2023, 10:04:51 AM