Testing Your Code
Prove it works, and keep it working.
Last month, Ava fixed a small bug in her discount code, and accidentally broke the delivery fee. She found out when a customer complained. Professionals protect themselves with automated tests, small programs that check the code, in seconds, every time something changes. If a test fails, you know immediately.

- A fix in one place breaks another
- Bugs found by angry customers
- Automated tests find them first
Assertions
The simplest test is an assert statement. It says, this must be true. If it is, nothing happens. If not, Python raises an AssertionError. You can add a message. Here, Ava checks her discount function with a few cases. Silence means success.
def discount(price, percent):
return round(price * (1 - percent / 100), 2)
assert discount(100, 10) == 90
assert discount(50, 0) == 50
assert discount(19.99, 50) == 10.0, "half price"
print("all assertions passed")Traceback (most recent call last):
File "/privateasserts.py", line 6, in <module>
assert discount(19.99, 50) == 10.0, "half price"
^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: half priceunittest: the standard framework
For real projects, use a test framework. Python includes unittest. A test class has methods starting with test, and uses checks like assertEqual. Running it prints a dot for every passing test, and details for every failure. The same tests run again after every change, and protect you for years.
import unittest
def discount(price, percent):
return round(price * (1 - percent / 100), 2)
class TestDiscount(unittest.TestCase):
def test_ten_percent(self):
self.assertEqual(discount(100, 10), 90)
def test_zero(self):
self.assertEqual(discount(50, 0), 50)
if __name__ == "__main__":
unittest.main(verbosity=2)test_ten_percent (__main__.TestDiscount.test_ten_percent) ... ok test_zero (__main__.TestDiscount.test_zero) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Testing errors and a failure
You can also test that errors are raised, with assertRaises. And when a test fails, the output shows exactly which one, and the expected and actual values. Here we deliberately break one test, to see the report. A failing test is not bad news, it is the system working.
import unittest
def fee(total):
if total < 0:
raise ValueError("negative")
return 0 if total >= 50 else 5
class T(unittest.TestCase):
def test_free(self):
self.assertEqual(fee(60), 0)
def test_error(self):
with self.assertRaises(ValueError):
fee(-1)
def test_wrong(self):
self.assertEqual(fee(10), 0)
unittest.main(argv=["x"], exit=False)..F
======================================================================
FAIL: test_wrong (__main__.T.test_wrong)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/privatetest_fee.py", line 15, in test_wrong
self.assertEqual(fee(10), 0)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^
AssertionError: 5 != 0
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)pytest and good testing habits
Most professionals use pytest, a third party tool. Tests are simple functions with plain assert statements, and the output is beautifully clear. Whatever the tool, the habits are the same. Test one thing per test. Name tests by behaviour. Test edge cases, like zero, empty, and negative. Run tests often, and automatically, on every change.
Write your own tests
Your turn. Here is a function that doubles a number. Write three assert statements that check it, with a positive number, zero, and a negative number. Ready? Here is one solution. Three simple checks, and a final message when they all pass.
Write 3 asserts for double(x): a positive number, zero, and a negative number.
Show hint
assert double(4) == 8
Show solution
def double(x):
return x * 2
assert double(4) == 8
assert double(0) == 0
assert double(-3) == -6
print("tests passed")tests passed
Summary
Great job. You can now protect your code with assertions and unittest, check for errors, and read test failures. Pytest builds on the same ideas. Next, what do you do when something is wrong, and you do not know why? You debug, and you log.
Key takeaways- assert for quick checks
- unittest classes with test_ methods
- Test edge cases, run often