Basic testing infrastructure

This commit is contained in:
Eden Kirin
2023-09-20 09:56:52 +02:00
parent f57c4d4491
commit 6109630ed1
12 changed files with 451 additions and 22 deletions

0
tests/__init__.py Normal file
View File

109
tests/conftest.py Normal file
View File

@ -0,0 +1,109 @@
import asyncio
import logging
from datetime import datetime
from typing import AsyncGenerator
import msgspec
import pytest_asyncio
import sqlalchemy
from _pytest.config import Config
from sqlalchemy import NullPool, event
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from app.lib import settings
from app.lib.sqlalchemy_plugin import _default
from app.lib.test_extras.db_setup import TestingDatabaseSetup
# A Guide To Database Unit Testing with Pytest and SQLAlchemy
# https://coderpad.io/blog/development/a-guide-to-database-unit-testing-with-pytest-and-sqlalchemy/
db_connection_url = sqlalchemy.engine.URL.create(
drivername="postgresql+asyncpg",
username=settings.testing.DB_USER,
password=settings.testing.DB_PASSWORD,
host=settings.testing.DB_HOST,
port=settings.testing.DB_PORT,
database=settings.testing.DB_NAME,
)
engine = create_async_engine(
db_connection_url,
echo=settings.db.ECHO,
echo_pool=settings.db.ECHO_POOL,
json_serializer=msgspec.json.Encoder(enc_hook=_default),
max_overflow=settings.db.POOL_MAX_OVERFLOW,
pool_size=settings.db.POOL_SIZE,
pool_timeout=settings.db.POOL_TIMEOUT,
poolclass=NullPool if settings.db.POOL_DISABLE else None,
)
async_session_factory = async_sessionmaker(
engine, expire_on_commit=False, class_=AsyncSession
)
TestingAsyncSessionLocal = async_sessionmaker(
engine,
expire_on_commit=False,
autoflush=False,
autocommit=False,
class_=AsyncSession,
)
@pytest_asyncio.fixture(scope="function")
async def db_session() -> AsyncGenerator[AsyncSession, None]:
"""The expectation with async_sessions is that the
transactions be called on the connection object instead of the
session object.
Detailed explanation of async transactional tests
<https://github.com/sqlalchemy/sqlalchemy/issues/5811>
"""
async with engine.connect() as connection:
trans = await connection.begin()
async with TestingAsyncSessionLocal(bind=connection) as async_session:
nested = await connection.begin_nested()
@event.listens_for(async_session.sync_session, "after_transaction_end")
def end_savepoint(session, transaction):
nonlocal nested
if not nested.is_active:
nested = connection.sync_connection.begin_nested()
yield async_session
await trans.rollback()
await engine.dispose(close=True)
# @pytest.fixture(scope="session")
# def event_loop():
# """
# Creates an instance of the default event loop for the test session.
# """
# policy = asyncio.get_event_loop_policy()
# loop = policy.new_event_loop()
# yield loop
# loop.close()
pytest_plugins = (
# "app.lib.test_extras.db_plugins",
)
def pytest_configure(config: Config) -> None:
logging.info(f"Starting tests: {datetime.utcnow()}")
db_setup = TestingDatabaseSetup(options=settings.testing)
asyncio.run(db_setup.init_db())
print()
def pytest_unconfigure(config: Config) -> None:
logging.info(f"Ending tests: {datetime.utcnow()}")
db_setup = TestingDatabaseSetup(options=settings.testing)
asyncio.run(db_setup.tear_down_db())

41
tests/test_general.py Normal file
View File

@ -0,0 +1,41 @@
# A Guide To Database Unit Testing with Pytest and SQLAlchemy
# https://coderpad.io/blog/development/a-guide-to-database-unit-testing-with-pytest-and-sqlalchemy/
import pytest
from sqlalchemy import text, event
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from app.domain.city import City
from app.lib import settings
from app.lib.sqlalchemy_plugin import engine
import pytest
import pytest_asyncio
import sqlalchemy
from sqlalchemy.ext.asyncio import (
AsyncSession,
create_async_engine,
async_scoped_session,
AsyncConnection,
)
class TestGeneral:
@pytest.fixture(scope="function", autouse=True)
def setup_class(self, db_session):
self.db_session = db_session
# async def teardown_class(self):
# await self.session.rollback()
# await self.session.close()
@pytest.mark.asyncio
async def test_bla(self):
stmt = text("select * from cities")
result = await self.db_session.execute(stmt)
print("#"*100)
for c in result:
print(c)
print("#"*100)
assert True