Git Checkpoints and Testing Before You Trust a Script
Save points and practice runs for code that touches real files
In this lesson
Once a script starts changing real files, two habits protect you: Git gives your project save points you can return to, and tests are little practice runs that confirm the important pieces still work. This lesson keeps both practical — just enough Git workflow and just enough testing to make automation trustworthy.
Explain it like I’m 5
Git is the save-point system for your code; tests are quick practice runs that check the risky parts before you let them loose on real files.
Git: save points for your project
Git records snapshots of your project called commits. Each small, well-described commit is a save point you can return to if a change goes wrong — which matters most when a script is rewriting real files. You don’t need full Git training here, just the everyday loop.
git init # start tracking this project
git add file_tool.py # stage your changes
git commit -m "Add safe dry-run mode" # save a checkpoint
git log --oneline # see your save points
Tests: practice runs for your helpers
A test checks that a piece of code does what you expect. The cheapest test is an assert: a statement that must be true, or Python raises an AssertionError. Test the pure helper functions — the ones that just compute a result — so you can verify your logic without ever touching real files.
def clean_filename(name):
return name.strip().lower().replace(" ", "_")
# A few quick checks before trusting it on real files:
assert clean_filename(" My File.TXT ") == "my_file.txt"
assert clean_filename("Report 2024") == "report_2024"
print("All checks passed")
All checks passed
Write a test, then trust the helper
Reading a failing test is a skill: the message tells you what was expected versus what happened. Practise by working out the expected result by hand and asserting it.
Here's a helper that turns a title into a URL slug. Add two assert checks with the correct expected results, then keep the success line.
def slugify(title):
return title.strip().lower().replace(" ", "-")
# TODO: assert slugify("Hello World") == ?
# TODO: assert slugify(" Spaced Out ") == ?
print("All checks passed")
Work it out by hand: trimmed, lowercased, spaces become hyphens. "Hello World" → "hello-world" and " Spaced Out " → "spaced-out".
def slugify(title):
return title.strip().lower().replace(" ", "-")
assert slugify("Hello World") == "hello-world"
assert slugify(" Spaced Out ") == "spaced-out"
print("All checks passed")
All checks passed
Common mistake: Making one giant commit after many unrelated changes
It feels efficient to code for an hour and commit once at the end.
Commit in small, focused steps with clear messages. Each becomes a clean save point you can roll back to without losing unrelated work.
Common mistake: Testing only the happy path
The obvious case is easiest to check, so the odd inputs — empty strings, missing extensions — get skipped.
Add a test for the edge cases too. Bugs usually hide in the inputs you didn’t think to try.
Common mistake: Running tests against real, important folders
It’s convenient to point a test at the actual files the script will process.
Test pure helper functions, or use a small folder of throwaway sample data. Never let a test mutate data you can’t afford to lose.
Why prefer small, frequent commits?
Small commits give you fine-grained save points and an understandable history.
What does an assert statement check?
An assert verifies a condition and stops the program with an error when it’s false.
Why test pure helper functions separately from file-moving code?
Pure helpers return a result with no side effects, so you can test them without risking real data.
Mini exercise (medium)
Write a helper safe_name(name) that trims a filename, lowercases it, and replaces spaces with underscores — for example " My Report " becomes "my_report". Then it should pass tests on at least two inputs.
Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.
def safe_name(name):
# TODO: trim, lowercase, and replace spaces with underscores
return name
# TODO: add two asserts that prove it works
print("done")
Chain the string methods: name.strip().lower().replace(" ", "_").
def safe_name(name):
return name.strip().lower().replace(" ", "_")
assert safe_name(" My Report ") == "my_report"
assert safe_name("Notes 2024") == "notes_2024"
print("done")
assert safe_name(" My Report ") == "my_report", "trim, lowercase, and turn spaces into underscores"
assert safe_name("A B C") == "a_b_c", "every space should become an underscore"
print("✓ safe_name works!")