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
293: Writing Unit Tests with unittest
Imagine you're building a high-performance engine. You wouldn't just bolt every single part together, pour in the oil, and turn the key for the first time, hoping it doesn't explode. Instead, you'd test the fuel pump separately. You'd test the spark plugs. You'd verify the pistons move smoothly before they ever touch the crankshaft. If the engine fails to start later, you don't have to guess where the problem is; you already know the fuel pump works because you tested it in isolation.
That's exactly what unit testing is. In Python, the unittest module is your workbench. It allows you to isolate a tiny piece of logic—a single function or method—and prove it works exactly as expected before you plug it into the rest of your application.
Building Your Test Rig
To use unittest, you don't just write loose functions. You create a class that inherits from unittest.TestCase. This gives you access to a suite of "assertion" methods that act as your quality checks. I've found that the biggest mistake beginners make is forgetting that any method you want the test runner to execute must start with the word test. If you name it check_total, Python will just ignore it. Name it test_total, and it's officially part of the suite.
import unittest
# The code we are testing
def calculate_discount(price, discount_percent):
if not (0 <= discount_percent <= 100):
raise ValueError("Discount must be between 0 and 100")
return price * (1 - (discount_percent / 100))
# The Test Rig
class TestDiscountLogic(unittest.TestCase):
def test_standard_discount(self):
self.assertEqual(calculate_discount(100, 20), 80)
self.assertEqual(calculate_discount(50, 10), 45)
def test_zero_discount(self):
self.assertEqual(calculate_discount(100, 0), 100)
Checking Your Assumptions
In the example above, assertEqual is the heavy lifter. It's basically saying: "I expect the result of this function to be exactly 80. If it isn't, stop everything and tell me why." But real-world code isn't always about getting the right number; sometimes it's about failing correctly.
If your function is supposed to throw an error when given bad input, you can't use assertEqual because the code will crash before the assertion happens. Instead, we use assertRaises. I use this constantly when validating user input or API responses to make sure my "guard rails" are actually working.
def test_invalid_discount_raises_error(self):
with self.assertRaises(ValueError):
calculate_discount(100, 110) # 110% is impossible
Avoiding Repetitive Setup
As your tests grow, you'll notice you're doing the same boring setup in every single test method—like creating a database connection or initializing a complex object. This is where setUp() comes in. Python calls setUp() automatically before every single test method in the class. It's like cleaning your workbench before starting a new task so that a mistake in Test A doesn't bleed into Test B.
class TestShoppingCart(unittest.TestCase):
def setUp(self):
# This runs before every test method
self.cart = ShoppingCart()
self.cart.add_item("Book", 10.00)
def test_item_count(self):
self.assertEqual(len(self.cart.items), 1)
def test_total_price(self):
self.assertEqual(self.cart.get_total(), 10.00)
One last tip: don't try to test everything. Focus on the "edge cases"—the weird inputs, the empty lists, the negative numbers. That's where the bugs usually hide. If you only test the "happy path" where everything goes right, you aren't really testing; you're just confirming your code can do what it was designed to do.
📋 Practical Task
Exercise: Validating a Password Strength Checker
You have been handed a PasswordChecker class that is supposed to enforce security rules. However, the original developer didn't write any tests, and you suspect the logic is flawed. Your goal is to write a comprehensive test suite using the unittest module to verify its behavior.
The Requirements for a "Strong" Password:
- Must be at least 8 characters long.
- Must contain at least one digit.
- Must contain at least one uppercase letter.
# The code to test
class PasswordChecker:
def is_strong(self, password):
if len(password) < 8:
return False
if not any(char.isdigit() for char in password):
return False
if not any(char.isupper() for char in password):
return False
return True
Your Task:
- Create a test class
TestPasswordCheckerinheriting fromunittest.TestCase. - Use
setUp()`to initialize an instance ofPasswordChecker. - Write a test method that verifies a truly strong password (e.g., "SecurePass123") returns
True. - Write three separate test methods to verify that passwords failing each individual requirement (too short, no digits, no uppercase) return
False. - Run your tests using
unittest.main()to ensure everything passes.
There are no comments for now.