qahawk.com
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

Unit Testing in Banking Example

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!

Sharing is caring!
Authors
  • avatar
    Name
    Shubham Kakkad
    Twitter
    @LinkedIn
  • Writer at qahawk