Skip to content

Commit

Permalink
Merge pull request #85 from caarmen/split-test-fixtures
Browse files Browse the repository at this point in the history
Split test fixtures
  • Loading branch information
caarmen authored Dec 14, 2024
2 parents bf986e7 + 4644510 commit 9eb6059
Show file tree
Hide file tree
Showing 20 changed files with 190 additions and 177 deletions.
176 changes: 6 additions & 170 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,170 +1,6 @@
from pathlib import Path

import pytest
import pytest_asyncio
from fastapi.testclient import TestClient
from pytest_factoryboy import register
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from alembic import command
from alembic.config import Config
from slackhealthbot.data.database import connection as db_connection
from slackhealthbot.data.database.models import Base
from slackhealthbot.data.repositories.sqlalchemyfitbitrepository import (
SQLAlchemyFitbitRepository,
)
from slackhealthbot.data.repositories.sqlalchemywithingsrepository import (
SQLAlchemyWithingsRepository,
)
from slackhealthbot.domain.localrepository.localfitbitrepository import (
LocalFitbitRepository,
)
from slackhealthbot.domain.localrepository.localwithingsrepository import (
LocalWithingsRepository,
)
from slackhealthbot.domain.remoterepository.remotefitbitrepository import (
RemoteFitbitRepository,
)
from slackhealthbot.domain.remoterepository.remotewithingsrepository import (
RemoteWithingsRepository,
)
from slackhealthbot.main import app
from slackhealthbot.remoteservices.repositories.webapifitbitrepository import (
WebApiFitbitRepository,
)
from slackhealthbot.remoteservices.repositories.webapiwithingsrepository import (
WebApiWithingsRepository,
)
from slackhealthbot.routers.dependencies import get_db
from tests.testsupport.factories.factories import (
FitbitActivityFactory,
FitbitUserFactory,
UserFactory,
WithingsUserFactory,
)


@pytest.fixture
def sqlalchemy_declarative_base():
return Base


@pytest.fixture
def db_path(tmp_path: Path) -> str:
return str(tmp_path / "database.db")


@pytest.fixture
def async_connection_url(db_path):
return f"sqlite+aiosqlite:///{db_path}"


@pytest.fixture
def connection_url(db_path):
return f"sqlite:///{db_path}"


@pytest.fixture()
def apply_alembic_migration(
async_connection_url: str,
monkeypatch: pytest.MonkeyPatch,
):
with monkeypatch.context() as mp:
mp.setattr(db_connection, "connection_url", async_connection_url)
alembic_cfg = Config("alembic.ini")
command.upgrade(alembic_cfg, "head")


@pytest.fixture(autouse=True)
def setup_db(apply_alembic_migration, connection):
# This fixture ensures that the alembic migration is applied
# before the connection fixture is used.
pass


@pytest_asyncio.fixture
async def mocked_async_session(async_connection_url: str):
engine = create_async_engine(async_connection_url)
session: AsyncSession = async_sessionmaker(bind=engine)()
yield session
await session.close()


@pytest.fixture
def local_withings_repository(
mocked_async_session: AsyncSession,
) -> LocalWithingsRepository:
return SQLAlchemyWithingsRepository(db=mocked_async_session)


@pytest.fixture
def remote_withings_repository() -> RemoteWithingsRepository:
return WebApiWithingsRepository()


@pytest.fixture
def local_fitbit_repository(
mocked_async_session: AsyncSession,
) -> LocalFitbitRepository:
return SQLAlchemyFitbitRepository(db=mocked_async_session)


@pytest.fixture
def remote_fitbit_repository() -> RemoteFitbitRepository:
return WebApiFitbitRepository()


@pytest.fixture
def fitbit_repositories(
local_fitbit_repository: LocalFitbitRepository,
remote_fitbit_repository: RemoteFitbitRepository,
) -> tuple[LocalFitbitRepository, RemoteFitbitRepository]:
return local_fitbit_repository, remote_fitbit_repository


@pytest.fixture
def client(mocked_async_session) -> TestClient:
app.dependency_overrides[get_db] = lambda: mocked_async_session
return TestClient(app)


@pytest.fixture
def withings_factories(
user_factory: UserFactory,
withings_user_factory: WithingsUserFactory,
) -> tuple[UserFactory, WithingsUserFactory]:
return user_factory, withings_user_factory


@pytest.fixture
def fitbit_factories(
user_factory: UserFactory,
fitbit_user_factory: FitbitUserFactory,
fitbit_activity_factory: FitbitActivityFactory,
) -> tuple[UserFactory, FitbitUserFactory, FitbitActivityFactory]:
return user_factory, fitbit_user_factory, fitbit_activity_factory


@pytest.fixture(scope="function", autouse=True)
def setup_factories(mocked_session):
for factory in [
UserFactory,
WithingsUserFactory,
FitbitUserFactory,
FitbitActivityFactory,
]:
# The _meta attribute is documented:
# https://factoryboy.readthedocs.io/en/stable/reference.html#factory.Factory._meta
# noinspection PyProtectedMember
factory._meta.sqlalchemy_session = mocked_session
# noinspection PyProtectedMember
factory._meta.sqlalchemy_session_persistence = "commit"


for factory in [
UserFactory,
WithingsUserFactory,
FitbitUserFactory,
FitbitActivityFactory,
]:
register(factory)
pytest_plugins = [
"tests.testsupport.fixtures.app",
"tests.testsupport.fixtures.db",
"tests.testsupport.fixtures.factories",
"tests.testsupport.fixtures.repositories",
]
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
FitbitUserFactory,
UserFactory,
)
from tests.testsupport.fixtures.builtins import freeze_time
from tests.testsupport.mock.builtins import freeze_time


@pytest.mark.asyncio
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_parse_sleep(input_filename: str, expected_sleep_data: SleepData):
input_file = (
Path(os.path.abspath(__file__)).parent.parent.parent.parent
/ "testsupport"
/ "fixtures"
/ "testdata"
/ input_filename
)
with open(input_file) as sleep_input:
Expand Down
2 changes: 1 addition & 1 deletion tests/routes/test_fitbit_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
FitbitUserFactory,
UserFactory,
)
from tests.testsupport.fixtures.fitbit_scenarios import activity_scenarios
from tests.testsupport.testdata.fitbit_scenarios import activity_scenarios


@pytest.mark.asyncio
Expand Down
2 changes: 1 addition & 1 deletion tests/routes/test_fitbit_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
FitbitUserFactory,
UserFactory,
)
from tests.testsupport.fixtures.fitbit_scenarios import (
from tests.testsupport.testdata.fitbit_scenarios import (
FitbitActivityScenario,
FitbitSleepScenario,
activity_scenarios,
Expand Down
2 changes: 1 addition & 1 deletion tests/tasks/test_fitbit_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
FitbitUserFactory,
UserFactory,
)
from tests.testsupport.fixtures.fitbit_scenarios import activity_scenarios
from tests.testsupport.testdata.fitbit_scenarios import activity_scenarios


@pytest.mark.asyncio
Expand Down
2 changes: 1 addition & 1 deletion tests/tasks/test_fitbit_poll.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
FitbitUserFactory,
UserFactory,
)
from tests.testsupport.fixtures.fitbit_scenarios import (
from tests.testsupport.testdata.fitbit_scenarios import (
FitbitActivityScenario,
FitbitSleepScenario,
activity_scenarios,
Expand Down
2 changes: 1 addition & 1 deletion tests/tasks/test_post_daily_activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
FitbitUserFactory,
UserFactory,
)
from tests.testsupport.fixtures.builtins import freeze_time
from tests.testsupport.mock.builtins import freeze_time


@pytest.mark.asyncio
Expand Down
11 changes: 11 additions & 0 deletions tests/testsupport/fixtures/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import pytest
from fastapi.testclient import TestClient

from slackhealthbot.main import app
from slackhealthbot.routers.dependencies import get_db


@pytest.fixture
def client(mocked_async_session) -> TestClient:
app.dependency_overrides[get_db] = lambda: mocked_async_session
return TestClient(app)
56 changes: 56 additions & 0 deletions tests/testsupport/fixtures/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from pathlib import Path

import pytest
import pytest_asyncio
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from alembic import command
from alembic.config import Config
from slackhealthbot.data.database import connection as db_connection
from slackhealthbot.data.database.models import Base


@pytest.fixture
def sqlalchemy_declarative_base():
return Base


@pytest.fixture
def db_path(tmp_path: Path) -> str:
return str(tmp_path / "database.db")


@pytest.fixture
def async_connection_url(db_path):
return f"sqlite+aiosqlite:///{db_path}"


@pytest.fixture
def connection_url(db_path):
return f"sqlite:///{db_path}"


@pytest.fixture()
def apply_alembic_migration(
async_connection_url: str,
monkeypatch: pytest.MonkeyPatch,
):
with monkeypatch.context() as mp:
mp.setattr(db_connection, "connection_url", async_connection_url)
alembic_cfg = Config("alembic.ini")
command.upgrade(alembic_cfg, "head")


@pytest.fixture(autouse=True)
def setup_db(apply_alembic_migration, connection):
# This fixture ensures that the alembic migration is applied
# before the connection fixture is used.
pass


@pytest_asyncio.fixture
async def mocked_async_session(async_connection_url: str):
engine = create_async_engine(async_connection_url)
session: AsyncSession = async_sessionmaker(bind=engine)()
yield session
await session.close()
51 changes: 51 additions & 0 deletions tests/testsupport/fixtures/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest
from pytest_factoryboy import register

from tests.testsupport.factories.factories import (
FitbitActivityFactory,
FitbitUserFactory,
UserFactory,
WithingsUserFactory,
)


@pytest.fixture
def withings_factories(
user_factory: UserFactory,
withings_user_factory: WithingsUserFactory,
) -> tuple[UserFactory, WithingsUserFactory]:
return user_factory, withings_user_factory


@pytest.fixture
def fitbit_factories(
user_factory: UserFactory,
fitbit_user_factory: FitbitUserFactory,
fitbit_activity_factory: FitbitActivityFactory,
) -> tuple[UserFactory, FitbitUserFactory, FitbitActivityFactory]:
return user_factory, fitbit_user_factory, fitbit_activity_factory


@pytest.fixture(scope="function", autouse=True)
def setup_factories(mocked_session):
for factory in [
UserFactory,
WithingsUserFactory,
FitbitUserFactory,
FitbitActivityFactory,
]:
# The _meta attribute is documented:
# https://factoryboy.readthedocs.io/en/stable/reference.html#factory.Factory._meta
# noinspection PyProtectedMember
factory._meta.sqlalchemy_session = mocked_session
# noinspection PyProtectedMember
factory._meta.sqlalchemy_session_persistence = "commit"


for factory in [
UserFactory,
WithingsUserFactory,
FitbitUserFactory,
FitbitActivityFactory,
]:
register(factory)
Loading

0 comments on commit 9eb6059

Please sign in to comment.