Defensive Programming
: Write specifications for functions
: Modularize
: Check conditions for inputs/outputs
Testing / Validation
: Compare input / output
Debugging
: Study events leading up to an error
Unit testing
: validate each piece of program
: testing each function separately
Regression testing
: add test for bugs
: catch reintroduced errors -> return to step 1
Integration testing
: does overall program work?
: catch error -> return to step 1
Black box testing
: explore paths through specification
Glass box testing
: explore paths through code
Designed without looking at the code
Paths through specification
: build test cases in different natural space partitions
: also consider boundary conditions (empty lists, singleton list, large numbers, small numbers)
Input을 넣었을 때 Output이 어떻게 되는가?
Branches
: exercise all parts of a conditional
For loops
: loop not entered
: body of loop executed exactly once
: body of loop executed more than once
While loops
: same as for loops, cases that catch all ways to exit loop
def abs(x) :
"""
Assumes x is an int
Returns x if x >= 0 and -x otherwise
"""
if x < -1 :
return -x
else :
return x
: a path-complete test suite coud miss a bug
: path-complete test suite : 2 and -2
: but abs(-1) incorrectly returns -1 -> should still test boundary cases
IndexError
: Trying to access beyond list limits
test = [1, 7, 4]
test[4]
TypeError
: Trying to convert an inappropriate type
int(test)
NameError
: Referencing a non-existing variable
TypeError
: Mixing data types without coercion (강제)
SyntaxError
: Python can't parse program
AttributionError
: attribute reference fails
ValueError
: operand type okay, but value is illegal
IOError
: IO system reports malfunction (e.g. file not found)
try :
a = int(input("Tell me one number:"))
b = int(input("Tell me another number:"))
print(a/b)
except :
print("Bug in user input.")
Exceptions raised by any statement in body of try are handled by the except statement and execution continues with the body of the except statement
예를 들어 a와 b 모두 정수를 입력해주면 print(a/b) 가 정상적으로 실행되고, a나 b에 float number를 넣어주면 (ValueError) except : 이후의 코드를 실행한다.
try :
a = int(input("Tell me one number:"))
b = int(input("Tell me another number:"))
print("a/b = ", a/b)
print("a+b = ", a+b)
except ValueError :
print("Could not convert to a number")
except ZeroDivisionError :
print("Can't divide by zero")
except :
print("Something went very wrong.")
raise <exceptionname> (<arguments>
// exceptionname : value of error you want to raise
// arguments : optional, but typically a string with a message
// example
raise ValueError("something is wrong")
def get_ratios(L1, L2) :
"""
Assumes : L1 and L2 are lists of equal length of numbers
Returns : a list containing L1[i]/L2[i]
"""
ratios = []
for index in range(len[L1]) :
try :
ratios.append(L1[index] / L2[index])
except ZeroDivisionError :
ratios.append(float('nan')) # nan = not a number
except :
raise ValueError('get_ratios called with bad arg')
return ratios
Want to be sure that assumptions on state of computation are as expected
: Use an assert statement to raise an AssertonError exception if assumptions not met
An example of good defensive programming
Format :
def avg(grades) :
assert len(grades) != 0, 'no grades data'
return sum(grades) / len(grades)
Assertions don't allow a programmer to control response to unexpected conditions
Ensure that execution halts whenever an expected condition is not met
Typically used to check inputs to functions, but can be used anywhere
Can be used to check outputs of a function to avoid propagating bad values
Can make it easier to locate a source of a bug