1 The following exercises do not contain solutions. Yet. Instead, we will be
2 asking you to submit your solutions to these exercises and then we will post up
3 solutions at the start of next week. We encourage you to discuss your approaches
4 or solutions on the course forum!
6 To submit your exercises, please create a `testing` folder in your personal
7 folder in the course repository. Place all of the code and files for theses
8 exercises in that folder and be sure to check it in.
11 ## Exercise 1: Mileage
13 The function 'convert_mileage' converts miles per gallon (US style) to liters
14 per 100 km (metric style):
17 gal_to_litre = 3.78541178
20 def convert_mileage(mpg):
21 '''Converts miles per gallon to liters per 100 km'''
22 litres_per_100_km = 100 / mpg / mile_to_km * gal_to_litre
23 return litres_per_100_km
26 Create a subdirectory in your version control directory called `testing`, then
27 copy this function into a file in that directory called `mileage.py`. Add more
28 code to that file to repeatedly ask the user for a mileage in miles per gallon,
29 and output the mileage in liters per 100 km, until the user enters the string
30 "`q`". You will need to use the `float()` function to convert from string to a
31 floating point number. Use the '`if __name__ == "__main__":`' trick to ensure
32 that the module can be imported without executing your testing code.
34 1. Copy `mileage.py` to create `tryexcept.py` Add a try/except block to the new
35 program to display a helpful message instead of crashing when users enter
36 invalid input (such as the number "0" or the name of their favorite hockey
39 2. Reading the function again, you realize that accepting 0 or negative values
40 make no sense and should be reported as an error. Look at the exceptions defined
41 in the `exceptions` module (use the built-in `help(...)` or `dir(...)`
42 functions) and decide which of Python's built-in exceptions is most appropriate
43 to use for invalid input. Create a copy of 'tryexcept.py' called 'raiser.py'
44 that raises this exception; modify the main body of your program to catch it;
45 and add a comment inside the file explaining why you chose the exception you
46 did. (Note: you have to call this file `raiser.py`, not `raise.py` because
47 'import raise' is an error. Â Can you see why?)
50 Google](http://www.google.ca/search?q=20+miles+per+gallon+in+litres+per+100+km&gbv=1),
51 20 miles per gallon are equivalent to 11.7607292 liters per 100 km. Use these
52 values to write a unit test. Keep in mind that these floating values are subject
53 to truncation and rounding errors. Save the test case in a file called
54 `test_mileage.py` and run it using the `nosetests` command. Â Note:
55 `test_mileage.py` should use '`from raiser import convert_mileage`' to get the
56 final version of your mileage conversion function.
58 4. Now add a second test case, for 40 miles per gallon equivalent to 5.88036458
59 liters per 100 km and run the tests again. Â Unless you have already fixed the
60 error that was present in the initial function, your test should fail. Â Find
61 and fix the error; submit your new function in a file called 'final_mileage.py'.
64 ## Exercise 2: Testing Averages
66 The results of a set of experiments are stored in a file, where the _i-th_ line
67 stores the results of the _i-th_ experiment as a comma-separated list of
68 integers. A student is assigned the task of finding the experiment with the
69 smallest average value. She writes the following code:
73 values = line.split(',')
81 def min_avg(file_name):
82 contents = open(file_name)
84 for (i, line) in enumerate(contents):
85 averages.append((avg_line(line), i))
88 min_avg, experiment_number = averages[0]
89 return experiment_number
92 1. Refactor `min_avg` so that it can be tested without depending on external
93 files. Submit your code in a file called `first_averages.py`.
95 2. Write Nose test cases for both functions. Consider what should happen if the
96 file is empty. Submit your tests in a file called `test_first_averages.py`.
97 Note: you may assume for now that all input is well formatted, i.e., you do
98 _not_ have to worry about empty lines, lines containing the names of hockey
101 3. The given specification is ambiguous: what should the result be if two or
102 more experiments are tied for the minimum average? Copy 'first_averages.py' to
103 create a new file 'second_averages.py'; modify it to handle this case; add a
104 comment to the top explaining what rule you decided to use; and create a file
105 'test_second_averages.py' that tests your changes.
107 4. Another student proposed an alternative implementation of the min_avg
111 def min_avg(file_name):
112 contents = open(file_name).readlines()
113 min_avg = avg_line(contents[0])
115 for (i,line) in enumerate(contents):
116 current_avg = avg_line(line)
117 if current_avg <= min_avg:
118 min_avg = current_avg
123 This implementation also finds an experiment with the smallest average, but
124 possibly a different one than the your function. Modify your test cases so that
125 both your implementation and this one will pass. (Hint: use the 'in' operator.)
127 5. One way to avoid the ambiguity of this specification is to define a
128 'min_avg_all' function instead, which returns a list with all the experiments
129 with the smallest average, and let the caller select one. Write tests for the
130 'min_avg_all' function, considering the following situations: an empty file,
131 exactly one experiment with minimum average, and more than one experiment with
132 minimum average. Keep in mind that in the last case, implementations could
133 return the list in different order. Write the tests the file "test_averages.py".
134 Use the same data as for the previous tests, if possible. You should use
135 variables to avoid code duplication. You don't need to implement the
136 'min_avg_all' function, but your test cases should be comprehensive enough to
137 serve as a specification for it.