Python Testing with unittest Examples
Short, runnable programs that show python testing in action. Read the explanation, press Run, then change the code and try your own ideas. New to the topic? Start with the Python Testing lesson.
1Plain asserts as a quick self-check
Before reaching for a framework, a handful of assert lines at the bottom of a file already guard a small function. A failing assert stops the script and shows your message.
def clamp(value, low, high):
return max(low, min(value, high))
assert clamp(5, 0, 10) == 5
assert clamp(-4, 0, 10) == 0
assert clamp(99, 0, 10) == 10
print("clamp works")
try:
assert clamp(15, 0, 10) == 15, "15 should be limited to 10"
except AssertionError as err:
print("AssertionError:", err)Expected outputclamp works
AssertionError: 15 should be limited to 10
2setUp gives every test a fresh shopping cart
setUp runs before each test, so tests never share state. Here self.cart is a brand-new object every time.
import io
import unittest
class Cart:
def __init__(self):
self.items = {}
def add(self, name, price, quantity=1):
self.items[name] = (price, quantity)
def total(self):
return sum(price * qty for price, qty in self.items.values())
class TestCart(unittest.TestCase):
def setUp(self):
self.cart = Cart()
def test_new_cart_is_empty(self):
self.assertEqual(self.cart.total(), 0)
def test_total_with_quantities(self):
self.cart.add("pen", 2, 3)
self.cart.add("book", 10)
self.assertEqual(self.cart.total(), 16)
result = unittest.TextTestRunner(stream=io.StringIO()).run(
unittest.defaultTestLoader.loadTestsFromTestCase(TestCart))
print(result.testsRun, result.wasSuccessful())Expected output2 True
3subTest: one test, many inputs
subTest labels each input, so a failure tells you which value broke instead of stopping at the first one.
import io
import unittest
def grade(score):
if score >= 90:
return "A"
if score >= 75:
return "B"
return "C"
class TestGrade(unittest.TestCase):
def test_grade_table(self):
cases = [(95, "A"), (90, "A"), (80, "B"), (75, "B"), (40, "C")]
for score, expected in cases:
with self.subTest(score=score):
self.assertEqual(grade(score), expected)
result = unittest.TextTestRunner(stream=io.StringIO()).run(
unittest.defaultTestLoader.loadTestsFromTestCase(TestGrade))
print("passed:", result.wasSuccessful(), "failures:", len(result.failures))Expected outputpassed: True failures: 0
4assertRaisesRegex checks the error message too
Checking only the exception type can pass for the wrong reason. assertRaisesRegex also matches the message text.
import io
import unittest
def withdraw(balance, amount):
if amount > balance:
raise ValueError(f"insufficient funds: balance {balance}, requested {amount}")
return balance - amount
class TestWithdraw(unittest.TestCase):
def test_ok(self):
self.assertEqual(withdraw(100, 30), 70)
def test_too_much(self):
with self.assertRaisesRegex(ValueError, "insufficient funds"):
withdraw(50, 80)
result = unittest.TextTestRunner(stream=io.StringIO()).run(
unittest.defaultTestLoader.loadTestsFromTestCase(TestWithdraw))
print(result.testsRun, "tests,", "all passed" if result.wasSuccessful() else "some failed")Expected output2 tests, all passed
5Reading a failure report
A failing test stores a traceback string. The last line holds the two values that were compared, which is usually all you need to find the bug.
import io
import unittest
def shipping_cost(weight_kg):
return 5 + weight_kg * 2
class TestShipping(unittest.TestCase):
def test_two_kilos(self):
self.assertEqual(shipping_cost(2), 10)
result = unittest.TextTestRunner(stream=io.StringIO()).run(
unittest.defaultTestLoader.loadTestsFromTestCase(TestShipping))
test, report = result.failures[0]
print("failed test:", test.id().split(".")[-1])
print(report.strip().splitlines()[-1])Expected outputfailed test: test_two_kilos
AssertionError: 9 != 10
6Doctest on a text-cleaning helper
Doctest runs the >>> examples in a docstring. The summary tells you how many examples ran and how many failed.
import doctest
def clean_tag(tag):
"""Lowercase a tag and strip a leading hash sign.
>>> clean_tag("#Python")
'python'
>>> clean_tag(" #Testing ")
'testing'
>>> clean_tag("web")
'web'
"""
return tag.strip().lstrip("#").lower()
runner = doctest.DocTestRunner(verbose=False)
for test in doctest.DocTestFinder().find(clean_tag, "clean_tag", globs={"clean_tag": clean_tag}):
runner.run(test)
summary = runner.summarize(verbose=False)
print("attempted:", summary.attempted)
print("failed:", summary.failed)Expected outputattempted: 3
failed: 0
Practise next
Python Testing exercises → Back to the lesson
Python Functions examplesPython Exceptions: try, except, else and finally examplesPython AssertionError examplesPython Logging examples