Contract Testing in Python


Python clean code tip:

Use contract testing when you want to verify the same behavior for different implementations.

Example:

import json
import pathlib
from dataclasses import dataclass

import pytest


@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)


class JSONUserRepository:
    def __init__(self, file_path):
        self._users = json.load(pathlib.Path(file_path).open())

    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)


class UserRepositoryContract:
    @pytest.fixture
    def repository(self):
        raise NotImplementedError('Not Implemented Yet')

    @pytest.fixture
    def username(self):
        return 'johndoe'

    @pytest.fixture
    def user(self, username):
        return User(username=username)

    def test_added_user_is_retrieved_by_username(self, username, user, repository):
        repository.add(user)

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


class TestInMemoryUserRepository(UserRepositoryContract):
    @pytest.fixture
    def repository(self):
        return InMemoryUserRepository()


class TestInJSONUserRepository(UserRepositoryContract):
    @pytest.fixture
    def repository(self, tmp_path):
        users_file = tmp_path/"user.json"
        users_file.write_text(json.dumps([]))
        return JSONUserRepository(users_file)