최상위 conftest.py

@pytest.fixture(scope="session")
def event_loop():
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()
 
@pytest.fixture(scope="session")
def test_context_session_id():
    context_session_id = str(uuid4())
    context_token = set_session_context(context_session_id)
    yield context_session_id
    reset_session_context(context_token)
 
 
@pytest.fixture(
    scope="session", autouse=True
)  # 아직 참조되는곳이 없어도 반드시 실행되도록
async def test_db_session(test_context_session_id):
    test_config = get_settings()
    engine = create_async_engine(
        "{DB_URL}://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}".format(
            DB_URL="postgresql+asyncpg",
            DB_USER=test_config.POSTGRES_USER,
            DB_PASSWORD=test_config.POSTGRES_PASSWORD,
            DB_HOST=test_config.DB_HOST,
            DB_PORT=test_config.DB_PORT,
            DB_NAME=test_config.POSTGRES_DB,
        ),
        echo=True,
        pool_recycle=3600,
    )
    test_db_session_token = set_session_context(test_context_session_id)
    session_factory = async_sessionmaker(
        bind=engine, expire_on_commit=False, class_=AsyncSession
    )
    database_session = async_scoped_session(
        session_factory=session_factory, scopefunc=get_session_context
    )
    await initialize_test_data(database_session)
    yield database_session
    await database_session.close()
    await database_session.remove()
    reset_session_context(test_db_session_token)
    await engine.dispose()
 

initialize_test_data

from app.core.database import Base
from app.model import base  # noqa: F401
from .user import users
from .category import categories
 
 
async def initialize_test_data(session):
    for table in reversed(Base.metadata.sorted_tables):
        await session.execute(table.delete())
    await session.commit()
    for user in users:
        session.add(user)
    for category in categories:
        session.add(category)
    print(hex(id(session)))
 
    await session.commit()

위처럼 구성해서 초기데이터를 아예 처음부터 넣어주도록 구성

혹은 테스트 환경 구성용 test_db_session을 만들어 두었으니, 나중에 test 코드에서 받아다가 직접 넣어도 상관은 없음

발생했던 문제

test_db_session을 fastapi app이 끌고 들어가서 사용해버리는 문제

인터넷에서 코드를 그대로 쓰면 안되는 이유 app.dependency_override로 get_session 함수를 override해버리는 방식을 보고 따라서 구현했었다. 그러다보니 test환경의 session을 그대로 끌고 들어가서 실제 router 테스트 과정에서 갖다 써버리는 문제가 발생. test환경의 세션과 AsyncClient환경의 세션이 다르다보니 session_context도 달라지고, 그러다보니 commit이 수행되지 않음. 세션을 발행받은곳은 test 환경인데 AsyncClient환경에서 세션을 닫아버리려고 시도하는 등 꼬여버림

해결

테스트환경에서의 db세션과 AsyncClient 환경에서의 db세션을 분리. 나는 test용 환경변수 파일을 따로 만들어서 사용하고있지만, 혹시 환경변수를 override해서 사용하려고 하는 사람이 있다면 AsyncClient환경의 세션을 만들고 app.dependency_override를 통해 get_session 함수를 테스트 환경에서 fastapi app 용으로 따로 발행한 db session으로 오버라이드 해버리는 방법도 가능할 듯 하다