0%

Final Capstone: A Community Lending Library · practice

Test, Refactor, and Review the System

The system is finished. Every operation in the contract works, and the hidden checks have said so at each step.

That is not the same as you knowing it works.

Tests you did not write prove less than you think

Every lesson so far graded your code against checks written by someone else. Useful, and limited in a specific way: they tell you whether you matched a specification. They do not tell you whether you understand what you built.

Chapter 13 made the case for writing your own. This is where you do it on a system you designed, and the difference is that nobody has told you which cases matter. Choosing them is the work.

Where to look

A contract this size has more testable claims than anyone would write tests for. Three questions narrow it down.

What breaks quietly? The loan limit is derived by counting. If that count were wrong, nothing would crash: a member would simply be allowed one item too many, forever, and nobody would notice.

What has a boundary? Overdue is >, not >=. Chapter 13’s Lesson 5 argued that a case on each side of a boundary is worth more than ten cases in the middle.

What did you get wrong while writing it? That is the most honest source of a test. If something took you two attempts, the test that would have caught it belongs in the file.

At least one failure path

The contract asks for this specifically, and it is the half people skip.

Testing that a checkout works is easy and comforting. Testing that a refused checkout is refused for the right reason, and leaves nothing behind, is what tells you the guard clauses in Lesson 5 are in the right order:

from pythonland_test import raises


def test_a_second_borrower_is_refused():
    shelf = build_library()
    shelf.checkout("b1", "m1", 10)

    with raises(ItemUnavailableError) as caught:
        shelf.checkout("b1", "m2", 11)

    assert caught.value.item_id == "b1"

Lesson 8 gave that error its attributes so a test could say something exact. A test asserting only that “some error happened” would pass against all four refusals, which is another way of saying it checks almost nothing.

Why write your own tests when the hidden checks already pass?

Refactor behind them

Once the tests are green, look at your library.py with the freedom Chapter 13 described: the internal implementation can change while the public contract stays the same. Tests provide evidence, but they may miss a behavior the contract still requires.

Common candidates, though the answer may honestly be “nothing”:

  • A that makes several independent decisions may become clearer when one is extracted into a focused helper. Blank lines alone are not a reason to split it.

  • A comment explaining what a block does is often a method name waiting to happen.

  • Repeated lookups like self._items[item_id] in several methods might want a small private helper.

If you change anything, run the tests after each step rather than at the end. That is the entire discipline, and it is the reason the tests came first.

What to build now

tests/test_library.py is the editor’s file. Write at least five tests: the ordinary path, a refusal checked by its type and its attributes, an overdue boundary, and whatever else you would want to know still worked six months from now.

Use Run tests as you go. Submit when they pass.

Task

Write your own tests for the system, in tests/test_library.py.

At least five test , covering at least:

  • a member borrowing an item, and the item leaving available_items();

  • a second borrower being refused an already-loaned item with ItemUnavailableError, checked by both its type and its item_id or due_day attribute;

  • the overdue boundary, on the due day and the day after;

  • a return, and the item becoming available again.

Import raises from pythonland_test for the refusal. Each test arranges its own library, so no test depends on another having run.

Then read library.py again and refactor anything you would want tidier. Run tests after each change. If nothing needs changing, say so and leave it alone: a refactoring nobody needs is not an improvement.