Test behavior, not implementation


Python clean test tip:

Tests should check the behavior rather than the underlying implementation details.

Such tests are easier to understand and maintain. They're also more resistant to refactoring (helps prevent false negatives).

👇

from dataclasses import dataclass


@dataclass
class User:
    username: str


class InMemoryUserRepository:
    def __init__(self):
        self._users = []

    def add(self, user):
        self._users.append(user)

    def get_by_username(self, username):
        return next(user for user in self._users if user.username == username)


# BAD
def test_add():
    user = User(username="johndoe")
    repository = InMemoryUserRepository()
    repository.add(user)

    assert user in repository._users


def test_get_by_username():
    user = User(username="johndoe")
    repository = InMemoryUserRepository()
    repository._users = [user]

    user_from_repository = repository.get_by_username(user.username)

    assert user_from_repository == user


# GOOD
def test_added_user_can_be_retrieved_by_username():
    user = User(username="johndoe")
    repository = InMemoryUserRepository()
    repository.add(user)

    assert user == repository.get_by_username(user.username)