The Assert Statement
We finally obtained a correct version of the dow.py
day-of-the-week calculator, but we arrived by a rather cumbersome process of manually adding code. We determined several test cases that revealed bugs in our code, but we added each one individually. Why not automate this testing?
We can write what is sometimes called a test harness for the DoW
function. We will attempt to cover easier cases and the “corner cases” mentioned earlier. It is particularly important to test leap years and non-leap years, since that is a major potential source of errors.
We can recast our tests for dow.py in a somewhat ad hoc manner.
Setting up tests for our DoW function.
# -*- coding: utf-8 -*-
"""
This program computes the day of the week given a date in the Gregorian
calendar. The user inputs the day, month, and year as integers.
Author: K. Holcomb
Changelog: Initial version 2013-05-20
Changelog: Bugs introduced to help with teaching how to debug 2016-07-22
"""
#Tables for lookups
months=[0,3,3,6,1,4,6,2,5,0,3,5]
month_names=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
days=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]
def DoW(day, month, year):
D=day
M=months[month-1]
century = 100*(year//100) #integer division
Y=year-century
L = Y//4 #integer division
century_leap_year= century%400==0
if century_leap_year:
L+=1
leap_year = (century_leap_year) or (year%4==0 and year%100 > 0)
if leap_year and month<3:
L -= 1
if century==1400 or century==1800 or century==2200:
C=2
elif century==1500 or century==1900 or century==2300:
C=0
elif century==1600 or century==2000 or century==2400:
C=5
elif century==1700 or century==2100 or century==2500:
C=4
else:
print("This algorithm doesn't cover the century requested")
C=-1
W=(C+Y+L+M+D)%7
return days[W]
#Tests
print("Testing a list of dates")
testdays=[(30,5,2016),(14,2,2000),(14,2,1900),(4,7,1971),(4,7,1776)]
answerdays=[1,1,3,0,4]
for doW,date in enumerate(testdays):
day,month,year=date
answer=answerdays[doW]
print("DoW {:} -> Answer {:} ".format(DoW(day,month,year),days[answer]))
print("\n\nTesting first of each month")
day = 1
month = 1
year=2016
answerdays=[5,1,2,5,0,3,5,1,4,6,2,4]
counter=0
while month < 13:
print("DoW {:} -> Answer {:}".format(DoW(day,month,year),days[answerdays[counter]]))
month += 1
counter+=1
We can easily add individual days to the list of scattered test cases, and could repeat the while block to test additional individual years.
Assert
The assert
keyword, as its name implies, tests whether a conditional is True. If it is, execution continues. If it returns False, the statement throws an AssertionError
and execution ceases.
The syntax is assert
<conditional>, <optional message>. The message is printed if the assertion fails.
doW=DoW(30,5,2016)
assert doW=="Monday", f"Should be Monday, got {doW}"
NumPy adds some additional array assertion functions, several of which are particularly useful for floating-point numbers, which cannot be reliably compared for strict equality.
The assert statement is intended for debugging only; use standard try
and except
, or other validation methods such as if
statements, for checking production cases such as file errors, type errors, erroneous input to functions, and so forth. This is just good practice in general, but it is also because the Python interpreter can be told to suppress assert statements. One way to do so is to run from the command line with the -O
(optimize) option.
Example
Run the following code from the command line (or use !python in an iPython console, or open a terminal in JupyterLab), first with python assert_it.py
and then with python -O assert_it.py
. What should you do instead of using assert
in this code?
def avg(values):
assert len(values)!=0,"Length of input to avg cannot be 0"
return sum(values)/len(values)
values_1=[11.,9.,3.,8.,7.]
print(avg(values_1))
values_2=[]
print(avg(values_2))
Exercise Starting from the dow_buggy.py script, add assertions to test for bugs.
Setting up tests for our DoW function.
{< code-download file="/courses/python-introduction/scripts/dow_assert.py" lang=“python” >}}
Work through the bugs as you did before. Hint: you may need to play with working through next, step in, continue, going in and out of debugging mode, etc. Pay attention to the value of test_id.