- Published at
Unit Testing
Imagine you're a car mechanic. Before declaring a car safe to drive, would you test the entire car at once?
Imagine you’re a car mechanic. Before declaring a car safe to drive, would you test the entire car at once?
No! You’d check each part individually — the engine, brakes, steering, and so on. You want to make sure each component works perfectly on its own before putting everything together.
This is what unit testing is: testing the smallest component so that when they are put together, you know there is no problem with individual parts.
Now, let’s translate this to something we build in software. Imagine we’re building a banking app — something that needs to be as reliable as a well-tested car.
Real-World Software Example: Banking App Login System
Let’s break down our banking app’s login system into testable units:
Username Validation
Think of this like checking if someone has a valid ID before entering a bank. We need to verify:
- The ID isn’t blank (empty check)
- It’s long enough to be valid (length check)
- It only contains letters and numbers (alphanumeric check)
def validate_username(username):
if not username:
return "Username cannot be empty"
if len(username) < 5:
return "Username must be at least 5 characters"
if not username.isalnum():
return "Username must be alphanumeric"
return "Valid"
Each test has a specific purpose:
- Empty test: Makes sure users can’t submit blank usernames
- Short test: Ensures username meets minimum length
- Character test: Checks that only valid characters are used
- Valid test: Confirms that good usernames pass all checks
def test_username_validation():
# Test empty username
assert validate_username("") == "Username cannot be empty"
# Test short username
assert validate_username("joe") == "Username must be at least 5 characters"
# Test invalid characters
assert validate_username("user@123") == "Username must be alphanumeric"
# Test valid username
assert validate_username("john123") == "Valid"
Password Validation
def validate_password(password):
if len(password) < 8:
return "Password must be at least 8 characters"
if not any(c.isupper() for c in password):
return "Password needs an uppercase letter"
if not any(c.isdigit() for c in password):
return "Password needs a number"
if not any(c in '!@#$%^&*' for c in password):
return "Password needs a special character"
return "Valid"
def test_password_validation():
# Test short password
assert validate_password("Abc1!") == "Password must be at least 8 characters"
# Test missing uppercase
assert validate_password("password1!") == "Password needs an uppercase letter"
# Test missing number
assert validate_password("Password!") == "Password needs a number"
# Test missing special character
assert validate_password("Password1") == "Password needs a special character"
# Test valid password
assert validate_password("Password1!") == "Valid"
Authentication Check
def authenticate_user(username, password):
if not validate_username(username) == "Valid":
return False
if not validate_password(password) == "Valid":
return False
# In real life, this would check against a database
stored_password = get_stored_password(username)
if not stored_password:
return False
return check_password_hash(password, stored_password)
def test_authentication():
# Test valid login
assert authenticate_user("john123", "Password1!") == True
# Test invalid username
assert authenticate_user("j", "Password1!") == False
# Test invalid password
assert authenticate_user("john123", "weak") == False
# Test non-existent user
assert authenticate_user("fake_user", "Password1!") == False
Best Practices (With Real Examples)
Test One Thing at a Time
GOOD - Testing one specific case:
def test_password_uppercase_requirement():
result = validate_password("password123!")
assert result == "Password needs an uppercase letter"
BAD - Testing multiple things:
def test_password():
# Don't mix different validations in one test
validate_password("")
validate_password("short")
validate_password("nouppercase")
Edge Cases
def test_authentication_failures():
# Test system errors
with pytest.raises(DatabaseError):
authenticate_user("john123", "Password1!")
# Test rate limiting
for _ in range(5):
authenticate_user("john123", "wrong")
assert is_account_locked("john123") == True
Conclusion
Remember: Just as each car component needs to work perfectly on its own before assembly, each part of our login system must be thoroughly tested before we trust it with users’ security!
- Authors
-
-
- Name
- Shubham Kakkad
- Writer at qahawk
-