Mission Log works, and Rocket is ready to show it to the whole crew. Raven asks one question first: "How do you know it still works after your next change?"
Rocket opens his mouth, then closes it. "I would run it and click around," he admits. "Every time."
Raven points to the functions that return values. "What do you notice?" she asks. "make_entry, format_entry, search_entries and count_tags never print. They just give back answers. Answers are easy to check."
Nova glows a steady green. "I found a pattern," she says. "Last week's guard line means a test file can import Mission Log without starting the menu."
Rocket creates a new file called test_mission_log.py.
This file imports mission_log, so it must be saved in the same folder. Because main only runs under if __name__ == "__main__":, importing the module does not start the menu.
The save and load test switches LOG_FILE to a test file first, so it never touches your real log. Then it deletes the test file with os.remove.
# test_mission_log.py
# Checks the Mission Log functions. Run this file after every change.
import os
import mission_log
def test_make_entry():
entry = mission_log.make_entry("Fixed the clock", "build", "2026-04-12")
assert entry == {"date": "2026-04-12", "tag": "build", "text": "Fixed the clock"}
def test_format_entry():
entry = mission_log.make_entry("Fixed the clock", "build", "2026-04-12")
assert mission_log.format_entry(3, entry) == "3. 2026-04-12 [build] Fixed the clock"
def test_search_entries():
entries = [
mission_log.make_entry("Built the launch clock", "build", "2026-04-10"),
mission_log.make_entry("Tested the fuel gauge", "test", "2026-04-11"),
]
assert len(mission_log.search_entries(entries, "CLOCK")) == 1
assert len(mission_log.search_entries(entries, "test")) == 1
assert mission_log.search_entries(entries, "rocket") == []
def test_count_tags():
entries = [
mission_log.make_entry("Part one", "build", "2026-04-10"),
mission_log.make_entry("Part two", "build", "2026-04-10"),
mission_log.make_entry("New idea", "idea", "2026-04-11"),
]
assert mission_log.count_tags(entries) == {"build": 2, "idea": 1}
assert mission_log.count_tags([]) == {}
def test_save_and_load():
mission_log.LOG_FILE = "test_log.json" # never touch the real log
entries = [mission_log.make_entry("Saved and loaded", "test", "2026-04-12")]
mission_log.save_entries(entries)
assert mission_log.load_entries() == entries
os.remove("test_log.json")
test_make_entry()
test_format_entry()
test_search_entries()
test_count_tags()
test_save_and_load()
print("All Mission Log tests passed") Run test_mission_log.py. If every assert holds, you see one line:
All Mission Log tests passed
A good test file checks the normal case, an edge case and a missing case. Here are three more checks for count_tags, tried in a small file of their own.
# count_check.py: more tests for count_tags
def make_entry(text, tag, day):
"""Build one entry as a dictionary."""
return {"date": day, "tag": tag, "text": text}
def count_tags(entries):
"""A dictionary of how many entries have each tag."""
counts = {}
for entry in entries:
tag = entry["tag"]
counts[tag] = counts.get(tag, 0) + 1
return counts
log = [
make_entry("Part A", "test", "2026-04-10"),
make_entry("Part B", "test", "2026-04-10"),
make_entry("Part C", "note", "2026-04-11"),
]
counts = count_tags(log)
assert counts["test"] == 2
assert counts.get("build", 0) == 0
assert len(counts) == 2
print("count_tags passed") | Statement | True or false? |
|---|---|
| The guard line lets a test file import mission_log without starting the menu. | ? |
| Tests should write to your real mission_log.json. | ? |
| Functions that return values are easier to test than functions that print. | ? |
| You only need to run the tests once, when you first write them. | ? |
Mission Log now has a safety net. Tomorrow you will hunt for edge cases the tests did not cover.