Lab: Testing Functions
Preparation
a. Make sure you are booted into the Mac OSX side of the computer.
b. Once you are logged in, double click the icon named courses.ads.carleton.edu on your desktop. If you encounter any errors, have me or the prefect contact Mike Tie to set up your account.
c. Open up a Terminal, navigate to your StuWork directory, create a folder named lab-01-28, and change your directory to the newly created folder. (Remember that you should replace USERNAME with your actual username!)
$ cd /Volumes/courses/cs111-01-w19/StuWork/USERNAME/
$ mkdir lab-01-28
$ cd lab-01-28
d. Open Brackets and make sure you have your StuWork/USERNAME directory opened. (You will see all of its subfolders in the left panel if so.) If the folder is not opened, you can open it by selecting File -> Open Folder and navigating to courses, then cs111-01-w19, then StuWork, then USERNAME.
Exercise 1
In this exercise, let’s experiment with looking at documentation of various functions and libraries we’ve seen so far in Python.
a. In your terminal, open up the REPL and type:
>>> help(round)
b. Notice how the Python developers document their code. When you are finished reading, press the q key to quit and return to the REPL.
c. Try using help on various other built-in functions such as print or importing the math library and looking at math.ceil.
d. It is also possible to read the documentation for whole libraries. Exit out of the REPL and type the following into the Terminal and when you are finished press the q key to quit.
$ pydoc math
The pydoc command parses a Python file and generates a nice document summarizing all the contents of the file.
Exercise 2
Recall that we previously wrote a function called bound_between. Below is an implementation along with docstring documentation.
def bound_between(val, lower, upper):
"""Bounds the given val between lower and upper
Parameters:
val: The numeric value to be bounded
lower: The numeric lower bound
upper: The numeric upper bound
Returns:
A number (result) that satisfies
if val < lower,
then result is lower
if val > upper,
then result is upper
otherwise
result is val
The numeric type of the result is preserved
Preconditions:
lower <= upper
"""
total = val + lower + upper
return total - min(val, lower, upper) - max(val, lower, upper)
a. Save the above code in a file named my_functions.py.
b. To see that you can view the help, open up a REPL and type:
>>> from my_functions import bound_between
>>> help(bound_between)
c. You can also use pydoc to view the documentation of everything in the file. Try exiting out of the REPL and typing the following into the Terminal.
$ pydoc my_functions
d. It is good practice to add a module docstring at the top of the file, therefore add the following docstring to the top of the my_functions.py file.
"""A module consisting of functions for the documentation and testing lab
Author: Titus Klinge, YOUR NAMES HERE
"""
e. Try rerunning pydoc to see how it changed the documentation.
Exercise 3
Now let’s test our bound_between function to make sure it is doing what we claim it does.
a. Examine the following test code and save it in a file called test_bound_between.py.
"""A module consisting of tests for the documentation and testing lab
Author: Titus Klinge, YOUR NAMES HERE
"""
from my_functions import bound_between
def test_below_bounds():
"""Checks cases where val < lower"""
assert bound_between(-10, 0, 100) == 0
assert bound_between(-20, 0, 100) == 0
def test_above_bounds():
"""Checks cases where val > upper"""
assert bound_between(110, 0, 100) == 100
assert bound_between(120, 0, 100) == 100
def test_between_bounds():
"""Checks cases where lower < val < upper"""
assert bound_between(25, 0, 100) == 25
assert bound_between(50, 0, 100) == 50
assert bound_between(75, 0, 100) == 75
b. Try running the tests by running the following in the Terminal.
$ pytest
When you run pytest from the terminal, it will run every file that starts with test_ or ends with _test.py and treat every function that starts with test_ as a unit test. It then shows you the results of the test. To run pytest on a single file, you can pass the name of the file in as a parameter.
c. Do you think the unit tests are comprehensive enough? Discuss this with your partner and make a list of things you think should be added to the test suite. After you decided what things should be added, turn to a neighboring group and compare thoughts.
d. If you decided the three test cases need more assert statements add them.
e. If you came up with more test cases that should be checked that don’t fall into the three categories, create new functions for them.
f. Rerun the tests. Does it still pass?
g. If your tests still pass, add the following test case to the file and rerun the tests:
def test_type_preservation():
"""Checks if the output type is preserved"""
assert type(bound_between(50.0, 0, 100)) == float
assert type(bound_between(50, 0.0, 100)) == int
assert type(bound_between(50, 0, 100.0)) == int
h. What happened? Discuss with your partner what the issue is here.
i. Rewrite bound_between so that it also preserves the type of its return value.
Exercise 4
Finish implementing the following function and add it to your functions_lab.py file. You should replace the return "" line with the actual implementation.
def reverse_string(s):
"""Returns a string with the characters of s in the reverse order
Parameters:
s: A string
Returns:
A string (result) with the characters of s in the reverse order
Postconditions:
* len(result) == len(s)
* for all reasonable i, result[i] = s[len(s) - 1 - i]
"""
return "" # STUB
Exercise 5
Create a file test_reverse_string.py and write a series of test cases that would give you confidence that your solution to exercise 4 is correct. Be sure to run them with pytest after you are finished.
Exercise 6
Finish implementing the following function and add it to your functions_lab.py file. You should replace the pass line with the actual implementation.
def reverse_list(lst):
"""Reverses the order of the elements of the list lst
Parameters:
lst: A list
Returns:
Nothing
Postconditions:
Let old_list refer to the list before executing the function. Then
after executing this function:
* len(lst) = len(old_lst)
* for all reasonable i, lst[i] = old_list[len(old_list) - 1 - i]
"""
pass # STUB
Exercise 7
Create a file test_reverse_list.py and write a series of test cases that would give you confidence that your solution to exercise 6 is correct. Be sure to run them with pytest after you are finished. The assert statements in this exercise will need to be more carefully written since reverse_list does not return a list but rather modifies the given list.