Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
300: Measuring Coverage with coverage.py
I've been there. You've spent the afternoon writing tests, you run your suite, and you see a wall of green dots. Every single test passed. You feel confident, you merge the PR, and then—about ten minutes after it hits production—you get a Slack notification that the app is crashing for a specific subset of users.
The problem isn't that your tests failed; it's that your tests didn't actually execute the code that was breaking. This is the gap between "passing tests" and "coverage."
The Illusion of a Green Test Suite
# payment_processor.py
def calculate_fee(amount, currency):
rates = {"USD": 0.02, "EUR": 0.025, "GBP": 0.03}
if currency not in rates:
# I added this logic to handle unsupported currencies,
# but I forgot to test it.
raise ValueError(f"Unsupported currency: {currency}")
return amount * rates[currency]
# test_payment.py
def test_usd_fee():
assert calculate_fee(100, "USD") == 2.0
def test_eur_fee():
assert calculate_fee(100, "EUR") == 2.5
If you run these tests with pytest, they pass perfectly. You might think, "Great, the fee calculation works." But look closely at the calculate_fee function. I wrote a guard clause to handle unsupported currencies, but my tests only cover the "happy path" (USD and EUR). If a user tries to pay with JPY, the ValueError will trigger, and unless I've tested how the rest of the app handles that error, I've just shipped a potential crash.
Exposing the Blind Spots
This is where coverage.py comes in. It doesn't tell you if your code is *correct* (that's what the tests are for), but it tells you which lines of code were actually touched during the test run. It's a map of your ignorance.
First, you'll need to install it: pip install coverage. Instead of running your tests directly through pytest, you wrap the execution with the coverage tool:
coverage run -m pytest
coverage report
When you run this against the code above, you'll see a table showing that payment_processor.py has something like 75% coverage. The coverage report command lists the missing lines—specifically, the raise ValueError line. Now I know exactly where my testing is lacking.
Turning Data into Action
The terminal report is okay for small files, but once you're working on a real project, it's a nightmare to track line numbers manually. I always generate the HTML report. It's a game-changer because it lets you browse your source code in a browser with the executed lines highlighted in green and the missed lines in red.
coverage html
Now, I can open htmlcov/index.html, see that red line in the currency check, and realize I need to add a specific test case for the failure scenario:
def test_unsupported_currency():
import pytest
with pytest.raises(ValueError):
calculate_fee(100, "JPY")
Run the coverage command again, and that line turns green. I haven't necessarily proven the code is "perfect," but I've eliminated the blind spot. I'm no longer guessing if my tests are comprehensive; I have the data to prove it.
📋 Practical Task
Closing the Gaps in the Password Validator
You've been handed a PasswordValidator class that is supposed to enforce several security rules. The previous developer wrote a few tests, but they claim the "test coverage is high." Your job is to prove them wrong using coverage.py.
The Code:
# validator.py
class PasswordValidator:
def validate(self, password):
if len(password) < 8:
return False, "Too short"
if not any(char.isdigit() for char in password):
return False, "Must contain a number"
if not any(char.isupper() for char in password):
return False, "Must contain an uppercase letter"
if " " in password:
return False, "No spaces allowed"
return True, "Valid"
# test_validator.py
from validator import PasswordValidator
def test_short_password():
v = PasswordValidator()
assert v.validate("Ab1") == (False, "Too short")
def test_valid_password():
v = PasswordValidator()
assert v.validate("SecurePass123") == (True, "Valid")
Your Goal:
- Run
coverage run -m pyteston this code. - Run
coverage reportto identify which validation rules (theifstatements) are not being executed by the current tests. - Write additional test cases in
test_validator.pyto cover the missing scenarios (missing numbers, missing uppercase, and spaces). - Run coverage again and verify that
validator.pyreaches 100% coverage.
There are no comments for now.