Python Testing with unittest · Examples
Practical Python

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.

Plain asserts as a quick self-check
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.

setUp gives every test a fresh shopping cart
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.

subTest: one test, many inputs
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.

assertRaisesRegex checks the error message too
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.

Reading a failure report
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.

Doctest on a text-cleaning helper
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