5.3.3. Homework#
Use these questions after class to check your understanding of the chapter concepts and coding skills.
Sign in from the account menu before you start working on the homework.
Complete all true/false and coding questions before you submit the homework.
Launch Live Code (the rocket) to write and run code in the coding cells.
Multiple submissions are allowed. The highest score is kept.
1. Exceptions
A batch import should catch a bad record so the report can explain the problem instead of failing without context.
2. Specific Exceptions
Catching every exception with a bare except is usually the clearest way to communicate which failure a program expects.
3. Finally Blocks
A cleanup step that should run whether parsing succeeds or fails belongs naturally in a finally block.
4. Assertions
A small test can use assert to state that a function should return the expected business result.
5. Testing Scope
A unit test should rely on the current live weather API being online to test a pure temperature-conversion function.
### 6. Write pytest-style test functions
# letter_grade(score) below should return "A" for 90 and above, "B" for 80 to 89,
# and "C" below 80. Write three pytest-style test functions that use plain assert
# with a message:
# test_grade_a: letter_grade(95) == "A", message "95 should be an A"
# test_grade_b_boundary: letter_grade(80) == "B", message "80 should be a B"
# test_grade_c: letter_grade(72) == "C", message "72 should be a C"
# Then run the tests the way pytest would: loop over the list
# [test_grade_a, test_grade_b_boundary, test_grade_c], call each test inside
# try/except AssertionError, print f"PASS {test.__name__}" when it passes or
# f"FAIL {test.__name__}: {err}" when it fails, and count the results.
# Do not fix letter_grade; the failing test is the point.
# Print exactly these four lines:
# # PASS test_grade_a
# # FAIL test_grade_b_boundary: 80 should be a B
# # PASS test_grade_c
# # 2 passed, 1 failed
def letter_grade(score):
if score >= 90:
return "A"
elif score > 80:
return "B"
else:
return "C"
### Your code starts here.
### Your code ends here.
PASS test_grade_a
FAIL test_grade_b_boundary: 80 should be a B
PASS test_grade_c
2 passed, 1 failed
### 7. Guard division
# Define `percent(part, whole)` that raises `ValueError` when whole is zero. Print exactly these two lines:
# # 25.0
# # ValueError: whole cannot be zero
### Your code starts here.
### Your code ends here.
25.0
ValueError: whole cannot be zero
### 8. Run a parametrized test table
# @pytest.mark.parametrize runs one test over many (input, expected) rows.
# Do the same by hand: loop over cases, call shipping_fee(weight) for each row,
# and compare the actual fee with the expected fee.
# For each failing row, print f"FAIL shipping_fee({weight}): expected {expected}, got {actual}".
# After the loop, print how many cases passed. Do not fix shipping_fee.
# Print exactly these two lines:
# # FAIL shipping_fee(1): expected 5, got 8
# # 4 of 5 cases passed
def shipping_fee(weight):
if weight < 1:
return 5
elif weight <= 5:
return 8
return 12
cases = [(0.5, 5), (1, 5), (3, 8), (5, 8), (7.5, 12)]
### Your code starts here.
### Your code ends here.
FAIL shipping_fee(1): expected 5, got 8
4 of 5 cases passed
### 9. Test with a fake dependency
# average_rating(product_id, fetch) gets its ratings by calling fetch(product_id).
# The real fetch_reviews would call an external API, so a unit test must not use it.
# Instead of patching, pass in a fake:
# 1. Create an empty list named calls.
# 2. Define fake_fetch(product_id) that appends product_id to calls and returns
# canned data: [5, 4, 4, 3, 5] for "p1" and [] for "p2".
# 3. Write test_average_typical (average_rating("p1", fake_fetch) == 4.2) and
# test_average_no_reviews (average_rating("p2", fake_fetch) == 0.0).
# 4. Loop over [test_average_typical, test_average_no_reviews], call each test,
# and print f"PASS {test.__name__}". Then print the calls the fake recorded.
# Print exactly these three lines:
# # PASS test_average_typical
# # PASS test_average_no_reviews
# # fake_fetch called with: ['p1', 'p2']
def fetch_reviews(product_id):
raise RuntimeError("This would call the reviews API")
def average_rating(product_id, fetch):
ratings = fetch(product_id)
if len(ratings) == 0:
return 0.0
return round(sum(ratings) / len(ratings), 1)
### Your code starts here.
### Your code ends here.
PASS test_average_typical
PASS test_average_no_reviews
fake_fetch called with: ['p1', 'p2']
### 10. Validate a function input
# Define `clean_label(label)` that:
# - raises `TypeError("label must be a string")` if `label` is not a string
# - otherwise returns `label.strip().title()` (removes the outer spaces and capitalizes each word)
# Then print `clean_label(" q1 revenue ")`, and call `clean_label(42)` inside
# `try`/`except TypeError` and print the error. Print exactly these two lines:
# # Q1 Revenue
# # TypeError: label must be a string
### Your code starts here.
### Your code ends here.
Q1 Revenue
TypeError: label must be a string