Back to articles
May 21, 2026

Unit Testing Best Practices

Unit Testing Best Practices Unit tests are your safety net. They catch regressions early, document expected behavior, and give you the confidence to refactor. But poorly written tests can be worse…

Placeholder cover imagePhoto: Lorem Picsum / Unsplash

Unit Testing Best Practices

Unit tests are your safety net. They catch regressions early, document expected behavior, and give you the confidence to refactor. But poorly written tests can be worse than no tests at all. Here's how to write unit tests that actually help.

Follow the AAA Pattern

Every unit test should follow the Arrange-Act-Assert pattern, which makes tests readable and self-documenting.

def test_calculate_total_applies_discount():
    # Arrange
    cart = ShoppingCart()
    cart.add_item("laptop", price=1000)
    cart.add_item("mouse", price=25)
    coupon = Coupon(code="SUMMER25", discount=0.25)

    # Act
    total = cart.calculate_total(coupon)

    # Assert
    expected = (1000 + 25) * 0.75  # 768.75
    assert abs(total - expected) < 0.01

Test One Thing Per Test

Each test should verify a single behavior. If a test fails, you should immediately know what went wrong.

# Bad — testing multiple behaviors in one test
def test_user_login():
    # Tests validation, database lookup, password hashing, AND session creation

# Good — focused tests
def test_login_rejects_invalid_password():
def test_login_creates_session_on_success():
def test_login_locks_account_after_five_failures():

Use Descriptive Test Names

A test name should read like a sentence describing the expected behavior.

// Good test names that double as documentation
test("should return empty array when list is empty");
test("should throw error when dividing by zero");
test("should update user email and persist changes");

Test Edge Cases, Not Just Happy Paths

The happy path is the easiest case. The real value of testing comes from covering edge cases:

def test_calculate_average():
    # Happy path
    assert calculate_average([10, 20, 30]) == 20.0

    # Edge cases
    assert calculate_average([]) == 0.0          # Empty list
    assert calculate_average([42]) == 42.0       # Single element
    assert calculate_average([-5, 5]) == 0.0     # Negative numbers

Mock External Dependencies

Unit tests should test your code in isolation. Mock databases, APIs, and file systems.

from unittest.mock import patch, MagicMock

def test_send_notifications_uses_api():
    mock_api = MagicMock()
    mock_api.post.return_value.status_code = 200

    with patch("myapp.notifications.NotificationAPI", return_value=mock_api):
        send_notifications(["user1", "user2"])

    mock_api.post.assert_called_once()
    assert mock_api.post.call_args[0][1] == ["user1", "user2"]

Conclusion

Great tests are fast, readable, and focused. They should be so clear that a new team member can understand what your code is supposed to do just by reading them. Invest time in writing good tests — they pay dividends every time a regression strikes.