Unit tests, corner cases

How can we be so sure that the code we wrote is doing what we want it to?

Does our code work 100% of the time?

404 image

These questions can be answered by using something called units tests.

Assert Statements

assert 1 == 2 , "1 is not equal to 2."
AssertionError: 1 is not equal to 2.

Detailed traceback: 
  File "<string>", line 1, in <module>
404 image
assert 1 == 1 , "1 is not equal to 1."
print('Will this line execute?')
Will this line execute?


assert 1 == 2 , "1 is not equal to 2."
print('Will this line execute?')
AssertionError: 1 is not equal to 2.

Detailed traceback: 
  File "<string>", line 1, in <module>
assert 1 == 2 
AssertionError: 

Detailed traceback: 
  File "<string>", line 1, in <module>

Why?

404 image

What to test?

def exponent_a_list(numerical_list, exponent=2):
    new_exponent_list = list()
    
    for number in numerical_list:
        new_exponent_list.append(number ** exponent)
    
    return new_exponent_list


assert exponent_a_list([1, 2, 4, 7], 2) == [1, 4, 16, 49], "incorrect output for exponent = 2"


assert exponent_a_list([1, 2, 3], 3) == [1, 8, 27], "incorrect output for exponent = 3"


assert type(exponent_a_list([1,2,4], 2)) == list, "output type not a list"

False Positives

def bad_function(numerical_list, exponent=2):
    new_exponent_list = [numerical_list[0] ** exponent] # seed list with first element
    for number in numerical_list[1:]:
        new_exponent_list.append(number ** exponent)
    return new_exponent_list


assert bad_function([1, 2, 4, 7], 2) == [1, 4, 16, 49], "incorrect output for exponent = 2"
assert bad_function([2, 1, 3], 3) == [8, 1, 27], "incorrect output for exponent = 3"


bad_function([], 2)
IndexError: list index out of range

Detailed traceback: 
  File "<string>", line 1, in <module>
  File "<string>", line 2, in bad_function

Testing Functions that Work with Data

def column_stats(df, column):
   stats_dict = {'max': df[column].max(),
                 'min': df[column].min(),
                 'mean': round(df[column].mean()),
                 'range': df[column].max() - df[column].min()}
   return stats_dict
data = {'name': ['Cherry', 'Oak', 'Willow', 'Fir', 'Oak'], 
        'height': [15, 20, 10, 5, 10], 
        'diameter': [2, 5, 3, 10, 5], 
        'age': [0, 0, 0, 0, 0], 
        'flowering': [True, False, True, False, False]}
         
forest = pd.DataFrame.from_dict(data)
forest
name height diameter age flowering
0 Cherry 15 2 0 True
1 Oak 20 5 0 False
2 Willow 10 3 0 True
3 Fir 5 10 0 False
4 Oak 10 5 0 False


assert column_stats(forest, 'height') == {'max': 20, 'min': 5, 'mean': 12.0, 'range': 15}
assert column_stats(forest, 'diameter') == {'max': 10, 'min': 2, 'mean': 5.0, 'range': 8}
assert column_stats(forest, 'age') == {'max': 0, 'min': 0, 'mean': 0, 'range': 0}

Systematic Approach

We use a systematic approach to design our function using a general set of steps to follow when writing programs.

1. Write the function stub: a function that does nothing but accepts all input parameters and returns the correct datatype.

def exponent_a_list(numerical_list, exponent=2):
    return list()

2. Write tests to satisfy the design specifications.

def exponent_a_list(numerical_list, exponent=2):
    return list()
   
assert type(exponent_a_list([1,2,4], 2)) == list, "output type not a list"
assert exponent_a_list([1, 2, 4, 7], 2) == [1, 4, 16, 49], "incorrect output for exponent = 2"
assert exponent_a_list([1, 2, 3], 3) == [1, 8, 27], "incorrect output for exponent = 3"
AssertionError: incorrect output for exponent = 2

Detailed traceback: 
  File "<string>", line 1, in <module>

3. Outline the program with pseudo-code.

def exponent_a_list(numerical_list, exponent=2):

    # create a new empty list
    # loop through all the elements in numerical_list
    # for each element calculate element ** exponent
    # append it to the new list 
    
    return list()
    
assert type(exponent_a_list([1,2,4], 2)) == list, "output type not a list"
assert exponent_a_list([1, 2, 4, 7], 2) == [1, 4, 16, 49], "incorrect output for exponent = 2"
assert exponent_a_list([1, 2, 3], 3) == [1, 8, 27], "incorrect output for exponent = 3"
AssertionError: incorrect output for exponent = 2

Detailed traceback: 
  File "<string>", line 1, in <module>

4. Write code and test frequently.

def exponent_a_list(numerical_list, exponent=2):
    new_exponent_list = list()
    
    for number in numerical_list:
        new_exponent_list.append(number ** exponent)
    
    return new_exponent_list
    
assert type(exponent_a_list([1,2,4], 2)) == list, "output type not a list"
assert exponent_a_list([1, 2, 4, 7], 2) == [1, 4, 16, 49], "incorrect output for exponent = 2"
assert exponent_a_list([1, 2, 3], 3) == [1, 8, 27], "incorrect output for exponent = 3"

5. Write documentation.

def exponent_a_list(numerical_list, exponent=2):
    """ Creates a new list containing specified exponential values of the input list. 
    
    Parameters
    ----------
    numerical_list : list
        The list from which to calculate exponential values from
    exponent : int or float, optional
        The exponent value (the default is 2, which implies the square).
    
    Returns
    -------
    new_exponent_list : list
        A new list containing the exponential value specified of each of
        the elements from the input list 
        
    Examples
    --------
    >>> exponent_a_list([1, 2, 3, 4])
    [1, 4, 9, 16]
    """
    new_exponent_list = list()
    for number in numerical_list:
        new_exponent_list.append(number ** exponent)
    return new_exponent_list

Let’s apply what we learned!