GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing zetcode.com — Python assert - working with assertions in Python Python assertlast modified October 30, 2026 Python 3.15 In this article we show how to work with assertions in Python. We define assertions, explain the difference between assertions and exceptions and show their relation to unit tests. AssertionsAssertions are internal self-checks in the code; they help developers find bugs as soon as possible. The purpose of assertions is to detect problems early in the program, where the cause is clear, rather than letting them pop up later as a side effect of some other operation. Assertions should not be used for data processing or validation, because they can be turned off. Some programming languages, such as Go, do not have assertions. Other languages have assert statements, but they are used less often (Java, where they are disabled by default).
Note: Go designers explain that developers should think more
about proper error handling and reporting. Go has assertions in unit tests only.
Assertions vs exceptionsAssertions are boolean expressions that must be true for the code to be correct. An assertion failure means there is a bug, and the fix is to correct the code. Exceptions indicate non-typical situations that can occur at runtime even in correct code. For instance, if the Internet connection is down, or the file permissions are not sufficient, the program has to deal with it. Assertions should not be used for handling runtime errors. Design by contractDesign by contract, or programming by contract, is a software development practice in which developers define formal, precise and verifiable interface specifications for software components. The specifications are divided into preconditions, postconditions and invariants. A precondition is a predicate that must always be true prior to the execution of a piece of code. A postcondition is a predicate that must always be true after the execution of a piece of code. An invariant is a property which remains unchanged after all operations. The design by contract approach was developed in the 1980s by Bertrand Meyer for the Eiffel language and was inspired by business contracts. Assertions and unit testsAssertions and unit tests overlap in some areas, but there are some differences. Assertions are assumptions about the internal state of the program, while unit tests examine the external behavior of a module. Unit tests work with test data, while assertions do not. Assert statements are used in unit tests. Classic assertions are assumptions about something which is true, while in unit tests, developers often test for false conditions as well. Python assert
The assert condition, message
If the condition is false, Python raises an if __debug__:
if not condition:
raise AssertionError(message)
The built-in Python assert preconditionA precondition checks the arguments of a function before it does its work. precondition.py
def info(age, name):
assert 18 <= age <= 65, 'age value must be between values 18 and 65'
print(f'{name} is {age} years old')
info(34, 'John')
info(84, 'Peter')
In the example, we have a precondition about the age value. The chained
comparison info(84, 'Peter') The second call violates the precondition. The traceback shows where the assertion failed and the carets point to the failing condition. $ python precondition.py
John is 34 years old
Traceback (most recent call last):
File "/home/user/python/precondition.py", line 9, in <module>
info(84, 'Peter')
~~~~^^^^^^^^^^^^^
File "/home/user/python/precondition.py", line 3, in info
assert 18 <= age <= 65, 'age value must be between values 18 and 65'
^^^^^^^^^^^^^^^
AssertionError: age value must be between values 18 and 65
Python assert postconditionA postcondition checks the result of a function before it is returned. postcondition.py
def do_discount(price, discount):
discounted_price = price - discount * price
assert 0 < discounted_price < price, \
f'discounted price {discounted_price} out of range'
return discounted_price
print(do_discount(120, 0.2))
try:
print(do_discount(120, 1.2))
except AssertionError as e:
print(f'AssertionError: {e}')
The example has a postcondition about a discounted price. It has to be greater than zero and lower than the original price. assert 0 < discounted_price < price, \
f'discounted price {discounted_price} out of range'
The message can be any expression, including an f-string that shows the offending value. A long assert statement can be continued on the next line with a backslash. print(do_discount(120, 1.2))
A discount of 120% produces a negative price, so the postcondition fails. We
catch the $ python postcondition.py 96.0 AssertionError: discounted price -24.0 out of range Python assert invariantAn invariant is a condition that holds for an object after every operation. The next class keeps its items sorted and checks this after each change. invariant.py
import bisect
class SortedList:
def __init__(self):
self._items = []
def _check(self):
assert all(a <= b for a, b in zip(self._items, self._items[1:])), \
f'items not sorted: {self._items}'
def add(self, value):
bisect.insort(self._items, value)
self._check()
def add_fast(self, value):
self._items.append(value)
self._check()
def __repr__(self):
return f'SortedList({self._items})'
vals = SortedList()
for n in [5, 1, 4, 2]:
vals.add(n)
print(vals)
vals.add_fast(3)
The def add(self, value):
bisect.insort(self._items, value)
self._check()
The def add_fast(self, value):
self._items.append(value)
self._check()
The $ python invariant.py
SortedList([1, 2, 4, 5])
Traceback (most recent call last):
File "/home/user/python/invariant.py", line 32, in <module>
vals.add_fast(3)
~~~~~~~~~~~~~^^^
File "/home/user/python/invariant.py", line 19, in add_fast
self._check()
~~~~~~~~~~~^^
File "/home/user/python/invariant.py", line 10, in _check
assert all(a <= b for a, b in zip(self._items, self._items[1:])), \
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: items not sorted: [1, 2, 4, 5, 3]
Assertions are removed with -OSince assertions can be disabled, they must never be used to validate input or to guard against conditions that can happen in correct programs. withdraw.py
def withdraw(balance, amount):
assert amount > 0, 'amount must be positive'
return balance - amount
print(__debug__)
print(withdraw(100, -50))
The
We run the program normally and with the $ python withdraw.py
True
Traceback (most recent call last):
File "/home/user/python/withdraw.py", line 7, in <module>
print(withdraw(100, -50))
~~~~~~~~^^^^^^^^^^
File "/home/user/python/withdraw.py", line 2, in withdraw
assert amount > 0, 'amount must be positive'
^^^^^^^^^^
AssertionError: amount must be positive
$ python -O withdraw.py
False
150
Validating input with exceptionsInput validation belongs in regular code that raises an exception. The checks then work regardless of the interpreter options. withdraw_checked.py
def withdraw(balance, amount):
if amount <= 0:
raise ValueError(f'amount must be positive, got {amount}')
if amount > balance:
raise ValueError('insufficient funds')
return balance - amount
for amount in [30, -50, 500]:
try:
print(withdraw(100, amount))
except ValueError as e:
print(f'error: {e}')
The function raises a $ python withdraw_checked.py 70 error: amount must be positive, got -50 error: insufficient funds The tuple pitfall
The tuple_pitfall.py
x = -1
assert (x > 0, 'x must be positive')
print('the assertion did not fail')
The assertion never fails, even though $ python tuple_pitfall.py /home/user/python/tuple_pitfall.py:3: SyntaxWarning: assertion is always true, perhaps remove parentheses? assert (x > 0, 'x must be positive') the assertion did not fail Python assertions in unit tests
Python has two widely used unit test frameworks: the third-party
We test the following module. algo.py
def find_max(values):
_max = values[0]
for val in values:
if val > _max:
_max = val
return _max
def find_min(values):
_min = values[0]
for val in values:
if val < _min:
_min = val
return _min
We have two algorithms in a module. The functions are called
Pytest example
The following example uses the test_algo.py
import algo
def test_min():
values = (2, 3, 1, 4, 6)
val = algo.find_min(values)
assert val == 1
def test_max():
values = (2, 3, 1, 4, 6)
val = algo.find_max(values)
assert val == 6
We pass test data to the tested $ pytest test_algo.py ============================= test session starts ============================== platform linux -- Python 3.15.2, pytest-9.2.0, pluggy-1.7.0 rootdir: /home/user/python plugins: anyio-4.15.1 collected 2 items test_algo.py .. [100%] ============================== 2 passed in 0.01s =============================== Pytest assertion introspectionPytest rewrites assert statements in test modules. When an assertion fails, it shows the values of the subexpressions, so we do not need to write messages. test_failing.py
def find_max(values):
_max = 0
for val in values:
if val > _max:
_max = val
return _max
def stats(values):
return {'min': min(values), 'max': max(values), 'count': len(values) - 1}
def test_max_negative():
values = [-4, -2, -7]
assert find_max(values) == -2
def test_stats():
values = [2, 3, 1, 4, 6]
assert stats(values) == {'min': 1, 'max': 6, 'count': 5}
The module contains two buggy functions. The assert find_max(values) == -2
For the first test, pytest shows that assert stats(values) == {'min': 1, 'max': 6, 'count': 5}
For dictionaries, pytest reports which items differ. $ pytest test_failing.py
============================= test session starts ==============================
platform linux -- Python 3.15.2, pytest-9.2.0, pluggy-1.7.0
rootdir: /home/user/python
plugins: anyio-4.15.1
collected 2 items
test_failing.py FF [100%]
=================================== FAILURES ===================================
______________________________ test_max_negative _______________________________
def test_max_negative():
values = [-4, -2, -7]
> assert find_max(values) == -2
E assert 0 == -2
E + where 0 = find_max([-4, -2, -7])
test_failing.py:19: AssertionError
__________________________________ test_stats __________________________________
def test_stats():
values = [2, 3, 1, 4, 6]
> assert stats(values) == {'min': 1, 'max': 6, 'count': 5}
E AssertionError: assert {'min': 1, 'm...6, 'count': 4} == {'min': 1, 'm...6, 'count': 5}
E
E Omitting 2 identical items, use -vv to show
E Differing items:
E {'count': 4} != {'count': 5}
E Use -v to get more diff
test_failing.py:25: AssertionError
=========================== short test summary info ============================
FAILED test_failing.py::test_max_negative - assert 0 == -2
FAILED test_failing.py::test_stats - AssertionError: assert {'min': 1, 'm...6...
============================== 2 failed in 0.02s ===============================
Unittest example
Now we test the same module with test_algo_unittest.py
import unittest
import algo
class AlgoTest(unittest.TestCase):
def test_min(self):
values = (2, 3, 1, 4, 6)
val = algo.find_min(values)
self.assertEqual(val, 1)
def test_max(self):
values = (2, 3, 1, 4, 6)
val = algo.find_max(values)
self.assertEqual(val, 6)
def test_empty(self):
with self.assertRaises(IndexError):
algo.find_max([])
if __name__ == '__main__':
unittest.main()
In the example, we use the $ python test_algo_unittest.py -v test_empty (__main__.AlgoTest.test_empty) ... ok test_max (__main__.AlgoTest.test_max) ... ok test_min (__main__.AlgoTest.test_min) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK SourcePython assert statement - language reference In this article we have worked with the Python assert statement and explained assertions in general. See also Python pytest and Python unittest. AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Python tutorials. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |