개인공부/트러블슈팅

[ Pytest ] fixture 'sync_client' not found, 정의한 fixture를 찾지 못하는 문제

KEEMSY 2024. 5. 4. 00:14

테스트 환경

개발환경
python==3.9
async-asgi-testclient==1.4.11
pytest==8.1.1
pytest-asyncio==0.23.6

 

디렉토리 구조
├── alembic
├── frontend
├── scripts
├── src
│   ├── domains
│   ├── external_service
│   ├── main.py
├── tests
│   ├── conftest.py
│   └── src
└── venv

상황

발생한 에러코드. 정의한 fixture 인 sync_client를 찾지 못하고 있다.

 

FastAPI를 활용한 동기 API 단위 테스트를 작성하던 중 정의한 fixture를 찾을 수 없다는 문제에 직면했다.  나는 conftest.py에 sync_client를 정의한 상태였는데, 해당 fixture를 찾아오지 못했다.

 

내가 설정한 conftest.py 에 정의 한 부분

 

이와 관련하여, 나는 conftest.py의 선언된 코드의 문제라고 생각했다. 하지만  다양한 pytest 예시 자료를 참고해도, 잘못된 부분을 찾을 수 없었다. 


조치 사항

코드를 import 함으로써 정상적으로 테스트가 성공함을 확인했다.

 

문제의 원인을 고민하던 중 에러 메시지를 해결하자 라는 생각으로, "해당 fixture를 import 해보자." 하는 아이디어로, 아래의 코드를 추가하여, 발생한 문제를 해결했다. 

추가한 코드
from tests.conftest import sync_client, sync_session
  • sync_client 만 import 할 경우, sync_client 에서 사용하는 sync_session을 찾지못하는 문제가 발생했다.
  • sync_client에서 사용하는 sync_session 까지 모두 import 선언을 해야 테스트가 정상적으로 실행 된다.

 

 


결론

찾지 못하는 fixture에 대하여, 직접 명시해줌으로써 문제를 해결할 수 있었다. 그러나 이것이 근본적인 문제의 원인을 해결한 것인 아닌 것 같다. 

  • import 코드는 왜 사용되고 있다고 인식 되지 않는가?(IDE 문제인가? 아니면 코드 상의 문제인가?)
  • 다른 예시들과는 다르게 왜 내 환경에서는 fixture를 찾지 못하는가?

 

예상되는 문제의 원인은 파일 디렉토리 구조 때문인듯한데, 디렉토리 구조가 파이썬의 Import에 어떤 영향을 주는지에 대해서는 아직 내가 잘 모른 상태이다.  하지만 무엇보다 내가 아직 pytest에 익숙하지 않아 이런 이슈를 겪고 있는듯하여, 공식문서를 참고하며 다양한 테스트를 작성하면서, pytest에 익숙해 져야겠다.

테스트에 사용한 코드
#test_sync_example.py

from src.domains.sync_example.database.models import SyncExample
from tests.conftest import sync_client, sync_session


def test_sync_example_생성(sync_client):
    res = sync_client.post("/api/sync/no-login/sync/example", json={"name": "test1", "description": "test"})
    new_sync_example = SyncExample(**res.json())

    assert res.status_code == 201
    assert new_sync_example.name == "test1"​

 

# conftest.py

import pytest
from starlette.testclient import TestClient

from src.database import Base, get_db
from src.main import app

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

SYNC_SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:test@localhost:3306/fastapi_test"

# sync_engine = create_engine(SYNC_SQLALCHEMY_DATABASE_URL, echo=True)
sync_engine = create_engine(SYNC_SQLALCHEMY_DATABASE_URL)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=sync_engine)


@pytest.fixture
def sync_session():
    Base.metadata.drop_all(bind=sync_engine)
    Base.metadata.create_all(bind=sync_engine)

    db = TestingSessionLocal()
    try:
        yield db
    finally:
        db.close()


@pytest.fixture
def sync_client(sync_session):
    def override_get_db():
        try:
            yield sync_session
        finally:
            sync_session.close()

    # app에서 사용하는 DB를 오버라이드하는 부분
    app.dependency_overrides[get_db] = override_get_db

    yield TestClient(app)​

 

728x90