Writing your first test

unittest

Python's standard library ships a full testing framework: unittest. Rather than scattering bare assert statements through your code, you organize related tests into a class, and use its built-in assertion methods:

import unittest

class TestMath(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(add(2, 3), 5)

    def test_negative_numbers(self):
        self.assertEqual(add(-1, -1), -2)

A few rules that matter:

  • The class subclasses unittest.TestCase — that's what gives it access to self.assertEqual and friends, and what lets the test runner recognize it as a set of tests to run.
  • Every test method's name starts with test_ — that's the convention the test runner uses to find them. A method named check_addition would silently never run.
  • Each test method takes self as its only parameter (like any method on a class) and takes no other arguments.

Common assertions

self.assertEqual(a, b)       # a == b
self.assertNotEqual(a, b)    # a != b
self.assertTrue(x)           # x is truthy
self.assertFalse(x)          # x is falsy
self.assertRaises(ValueError, some_function, arg)   # calling some_function(arg) raises ValueError

assertEqual is by far the one you'll use the most — it's a clearer, more informative version of assert a == b: when it fails, it tells you both values, not just "assertion failed".

What makes a test good, not just present

A test that never actually fails isn't testing anything — it's decoration. Consider testing is_positive(n) with only this:

def test_positive(self):
    self.assertEqual(is_positive(5), True)

That passes against a correct implementation — but it would also pass against the broken implementation def is_positive(n): return True (always returns True, ignoring n entirely)! The test never checks a case where the right answer is False, so it can't catch that specific bug.

A better test suite covers the boundaries and both outcomes of what you're testing — for is_positive, that means at least one case that should be True, one that should be False, and (since "positive" has a boundary) a careful look at what happens exactly at 0.

In the problems ahead, your test methods will be checked two ways: run against a correct implementation (every test should pass), and run against implementations with a specific bug (at least one of your tests needs to actually fail, proving it would have caught that bug in the first place). Writing tests that only pass — without ever being capable of catching a real mistake — won't be enough.