Skip to content

Testing with FennFlow

FennFlow is designed to be testable without mocks. Swap S3ConnectorConfig for InMemoryConnectorConfig and point the backend at an in-memory SQLite database — your tests run with zero infrastructure and zero mocks.

Example: Testing user registration with avatar upload

import pytest
import pytest_asyncio

from fennflow import ConfigDict, UnitOfWork
from fennflow.backends import InMemoryBackendConfig, SqlalchemyBackendConfig
from fennflow.connectors import InMemoryConnectorConfig, S3ConnectorConfig
from fennflow.files import ImageContent
from fennflow.repositories import CreateRepository, GetRepository, S3RepoField


class AvatarRepository(CreateRepository, GetRepository):
    pass


class AppUOW(UnitOfWork):
    avatars = S3RepoField(AvatarRepository, bucket_name="avatars")
    config = ConfigDict(
        backend=SqlalchemyBackendConfig(),
        connector=S3ConnectorConfig(),
    )


class TestUOW(AppUOW):
    config = ConfigDict(
        backend=InMemoryBackendConfig(),
        connector=InMemoryConnectorConfig(),
    )


class DbClient:
    async def create_user(self, user_id: str, avatar_path: str) -> None: ...


async def register_user(
    user_id: str,
    avatar: ImageContent,
    uow: AppUOW,
    db: DbClient,
) -> None:
    async with uow:
        await uow.avatars.at(user_id).create(avatar)
        await db.create_user(user_id=user_id, avatar_path=avatar.filename)


@pytest_asyncio.fixture
def db():
    return DbClient()


@pytest.mark.asyncio
async def test_register_user_saves_avatar(db):
    avatar = ImageContent(
        data=b"fake-image-bytes",
        media_type="image/jpeg",
        filename="avatar.jpg",
    )

    await register_user(user_id="user-123", avatar=avatar, uow=TestUOW(), db=db)

    async with TestUOW() as uow:
        response = await uow.avatars.at("user-123").get(avatar.filename)
        assert response.images[0].content.filename == "avatar.jpg"

TestUOW inherits everything from AppUOW and overrides only the config — same repositories, same business logic, different infrastructure. No mocks, no patching, no Docker.