What automated tests are for, how they differ from checking output by eye, and why they stop old bugs coming back.
About 30 minBeginner3 exercisesFree
You will be able to
Explain the difference between checking output by eye and an automated check
Describe what a regression is and how a test prevents one
Turn a manual print-and-look check into an assert that fails loudly
Choose test inputs that would expose a wrong implementation, not only confirm a right one
Imagine you maintain the checkout code for a small online bookshop. A colleague changes the VAT calculation to "tidy it up". Nobody notices anything wrong for three weeks, until the accountant finds that every invoice under 100 SAR was undercharged. The change took two minutes. Finding it, refunding, re-issuing invoices and explaining it to customers took days.
A test is code that runs your code with chosen inputs and checks the results automatically. If the colleague's change had broken a test, they would have seen a red failure seconds after making it, before it reached a single customer. This track is about writing tests like that, and about writing code that is easy to test and easy to change.
Checking by eye
Most people start by printing results and looking at them. That is a manual check: you run the code, read the output and decide whether it looks right.
Try it: Without a calculator, is 22.99 right for 19.99? Notice that you have to work out each answer yourself every time you run it.
Manual checks have three problems:
You must know the right answer each time and compare it in your head. After the tenth run you stop looking carefully.
They don't scale. A real program has hundreds of behaviours. Nobody re-checks all of them after every change.
They are forgotten. The checks live in your head, not in the project, so the next person can't run them.
An automated check writes the expected answer down once, in code. Python's assert statement does this: assert condition does nothing when the condition is true and stops the program with an AssertionError when it is false.
Try it: Change 22.99 to 23.0 and run it. The program stops at that line with an AssertionError instead of printing the final message.
Now the expected answers are part of the code. Anyone can run the checks at any time, and the computer does the comparing. Silence (or a final "passed" message) means every check held.
Regressions
A regression is a bug in something that used to work. Regressions usually come from changes that look harmless: a refactor, a "small" optimisation, a new feature that touches shared code. Here is the colleague's tidy-up. The answer for 100 SAR is still correct, so a quick manual check of one value would pass, but the automated checks catch it immediately.
A regression caught by an existing check
defadd_vat(price):
# "Simplified": VAT on 100 is 15, so add 15returnround(price +15,2)assert add_vat(100)==115.0assert add_vat(19.99)==22.99print("All checks passed")
Try it: Read the error: it points at the exact line whose expectation was broken. Delete the second assert and run again: with only the 100 SAR check, the bug slips through.
That last experiment is the most important idea in this track: a test is only as good as the bugs it can catch. Checking add_vat(100) alone cannot tell price * 1.15 apart from price + 15. Good tests use inputs where a wrong implementation would give a different answer: a price with halalas, a zero, a value just past a limit.
What bugs cost
The later a bug is found, the more it costs:
Found when…
Typical cost
Writing the code, by a failing test
Seconds: you fix it while the code is fresh in your mind
In code review
Minutes, plus a second person's time
By a tester before release
Hours: reproduce, report, fix, re-test
By customers in production
Days: refunds, data repair, support calls, lost trust
Tests move bug-finding to the top row. They also give you confidence to change code: if the tests still pass after an edit, you haven't broken the behaviours they describe.
Instead of loose asserts at the bottom of a file, put related checks in a function whose name starts with test. Then you can run them on demand, and (from the third lesson) a tool can find and run them all for you.
deftest_add_vat():
assert add_vat(100)==115.0assert add_vat(19.99)==22.99
test_add_vat()# run itprint("All tests passed")
In the exercises where you write tests, the grader does something extra: it runs your tests against the correct code (they must pass) and against several deliberately broken versions (at least one of your tests must fail for each). That is exactly the job of a real test suite.
Write the tests · ●●difficulty 2 of 5 · about 6 min
Tests that tell right from wrong
The function add_vat(price) adds 15% VAT and rounds to 2 decimal places: add_vat(100) is 115.0 and add_vat(19.99) is 22.99.
The starter test checks only 100 SAR, so a broken version that adds a flat 15 SAR would still pass. Extend test_add_vat with more assert lines so that it would catch:
an implementation that adds a fixed 15 instead of 15%,
an implementation that rounds to 1 decimal place instead of 2.
Keep the test passing for the real add_vat. Work out each expected value by hand; don't call add_vat to compute the expected answer.
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Debugging · ●●difficulty 2 of 5 · about 7 min
Make the team's checks pass
An attendance system marks staff as late. The agreed rules: a person is late only when they arrive after the start minute plus a grace period (5 minutes by default). Arriving exactly at the end of the grace period is on time. count_late counts late arrivals and must respect a custom grace when one is given.
A recent edit broke things. When you run the program, the team's automated checks at the bottom stop with an AssertionError. Fix is_late and count_late so every check passes. Don't change the checks: they describe the rules.
Times are minutes since midnight (480 is 08:00).
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Edge cases · ●●difficulty 2 of 5 · about 6 min
Keep a fixed bug fixed
Last week the product page crashed for products with no reviews, because average_rating([]) divided by zero. It has been fixed: now it returns 0.0 for an empty list, and otherwise the mean rounded to 1 decimal place (average_rating([4, 5]) is 4.5, average_rating([4, 4, 5]) is 4.3).
Write two test functions so the bug can never come back unnoticed:
test_average_rating_no_reviews — checks the empty-list case returns 0.0;
test_average_rating_typical — checks normal lists, including one whose mean is not a whole number and one that needs rounding.
Python code editor. Tab indents. Press Escape then Tab to leave the editor. Control or Command plus Enter runs the code; Shift plus Enter checks your answer.
Ctrl+Enter run · Shift+Enter check · your code is saved in this browser as you type
Output
Run your code to see its output, or Check to test it against the requirements.
The solution becomes available after you pass, or after all three hints and two checks.
Summary
A manual check needs you to know and compare the answer every time; an automated check writes the expected answer down once, in code.
assert condition is silent when the condition is true and raises AssertionError when it is false.
A regression is a bug in something that used to work; tests catch regressions the moment they are introduced.
A test is only as good as the bugs it catches: choose inputs where a wrong implementation would give a different answer.