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:
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.test_ — that's the convention the test runner uses to find them. A method named check_addition would silently never run.self as its only parameter (like any method on a class) and takes no other arguments.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".
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.