Learning Python with Advent of Code Walkthroughs

Dazbo's Advent of Code solutions, written in Python

The Python Journey - Unit Testing

Useful Links

Unit Testing in PythonAssertion

Unit Tests

Unit tests are small, coded, repeatable tests that execute a component of our code, and assert that the code does what we expected.

The idea is that unit tests are quick to repeat. So we can use them to:

unittest

Overview

Python includes a built-in unittest package. To use it:

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

Asserting

Method What it does Example
assertTrue Checks that the expression is True self.assertTrue(6 in self.s)
assertFalse Checks that the expression is False self.assertFalse(6 in self.s)
assertEqual Checks that the first parameter equals the second self.assertEqual(len(s), 0)
assertNotEqual You can guess this one! self.assertNotEqual(len(s), 0)
assertRaises Checks that statement raises an exception self.assertRaises(StopIteration, lambda: next(i))

Demo

Here I’m testing the answers to two Advent of Code parts, using the supplied test data:

""" Test our Rope_Bridge Solution """
from pathlib import Path
import unittest
import rope_bridge  # We need to import the code we will be testing

SAMPLE_INPUT_FILE = Path(Path(__file__).parent, "input/sample_input.txt")

class TestRopeBridge(unittest.TestCase):
    """ Set up data using the sample input.
    Then run two tests, asserting the correct length of the returned lists. """
    
    def setUp(self):
        # load the data
        with open(SAMPLE_INPUT_FILE, mode="rt") as f:        
            self.data = [(d, int(v)) for d, v in [instruction.split() for instruction in f.read().splitlines()]]
        
    def test_part_1(self):
        expected = 88
        self.assertEqual(len(rope_bridge.pull_rope(self.data, 2)), expected)
        
    def test_part_2(self):
        expected = 36
        self.assertEqual(len(rope_bridge.pull_rope(self.data, 10)), expected)

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

When we run it with the expected data, and it works, the output looks like this:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK

We can see that it ran two tests, and they both passed. Hurrah!

But what if our code has bugs? I’ve just changed my solution code so that it returns empty lists, instead of lists of the required lengths. Now, the output of our unit testing looks like this:

FF
======================================================================
FAIL: test_part_1 (__main__.TestRopeBridge)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "f:\Users\Darren\localdev\Python\Advent-of-Code\src\AoC_2022\d09_rope_bridge\test_rope_bridge.py", line 19, in test_part_1  
    self.assertEqual(len(rope_bridge.pull_rope(self.data, 2)), expected)
AssertionError: 0 != 88

======================================================================
FAIL: test_part_2 (__main__.TestRopeBridge)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "f:\Users\Darren\localdev\Python\Advent-of-Code\src\AoC_2022\d09_rope_bridge\test_rope_bridge.py", line 23, in test_part_2  
    self.assertEqual(len(rope_bridge.pull_rope(self.data, 10)), expected)
AssertionError: 0 != 36

----------------------------------------------------------------------
Ran 2 tests in 0.007s

FAILED (failures=2)

Now we can see that both tests failed. The test tells us what was expected, and what was actually received. So this is really useful!!