Showing Posts From

Fixture로

Pytest fixture로 테스트 데이터 관리하기: 야근 줄이는 법

Pytest fixture로 테스트 데이터 관리하기: 야근 줄이는 법

Pytest fixture로 테스트 데이터 관리하기: 야근 줄이는 법 야근의 시작 금요일 오후 6시. 퇴근 30분 전이다. "J님, 테스트 스위트 돌리는 데 왜 이렇게 오래 걸려요?" 개발팀장 질문이다. 답은 알고 있다. 매번 디비 초기화하느라 30분씩 날린다. "최적화 좀 해보겠습니다." 그렇게 주말을 픽스처 공부로 보냈다.문제는 반복이었다 월요일 출근. 테스트 코드를 다시 봤다. def test_user_login(): db = create_db_connection() db.clean_all_tables() db.insert_test_user() # 실제 테스트 result = login("test@test.com") assert result.success db.close()def test_user_logout(): db = create_db_connection() db.clean_all_tables() db.insert_test_user() # 실제 테스트 result = logout() assert result.success db.close()똑같은 셋업이 50개 테스트마다 반복된다. 디비 초기화가 30초씩 걸린다. 50개 × 30초 = 25분. 순수 셋업 시간만. "이거 미친 짓이었네." 픽스처 공부한 보람이 있다. 바로 리팩토링 시작했다. fixture 기본부터 conftest.py 파일을 만들었다. import pytest@pytest.fixture def db_connection(): db = create_db_connection() yield db db.close()yield가 핵심이다. 앞은 셋업, 뒤는 티어다운. 자동으로 실행된다. 테스트 코드가 간단해졌다. def test_user_login(db_connection): db = db_connection result = login("test@test.com") assert result.successclose()를 신경 쓸 필요가 없다. 픽스처가 알아서 정리한다. 첫 번째 개선. 5분 절약.scope로 시간 줄이기 문제는 여전했다. 매 테스트마다 디비 연결을 새로 만든다. scope를 알게 됐다. @pytest.fixture(scope="session") def db_connection(): db = create_db_connection() yield db db.close()session scope. 전체 테스트 스위트에서 한 번만 실행된다. 하지만 문제가 생겼다. 테스트끼리 데이터가 꼬인다. "아, 연결은 유지하되 데이터는 초기화해야 하는구나." 다시 수정했다. @pytest.fixture(scope="session") def db_connection(): db = create_db_connection() yield db db.close()@pytest.fixture(scope="function") def clean_db(db_connection): db_connection.clean_all_tables() return db_connection연결은 세션당 한 번. 테이블 초기화는 테스트마다. 실행 시간이 25분에서 8분으로 줄었다. 17분 절약. "이제 좀 사람 사는 거 같네." 테스트 데이터 픽스처 다음 문제. 테스트 데이터 준비가 중복됐다. def test_admin_access(clean_db): admin = User(email="admin@test.com", role="admin") clean_db.insert(admin) result = access_admin_page(admin) assert result.successdef test_admin_delete(clean_db): admin = User(email="admin@test.com", role="admin") clean_db.insert(admin) result = delete_user(admin) assert result.successadmin 유저 생성 코드가 계속 반복된다. 픽스처로 뺐다. @pytest.fixture def admin_user(clean_db): admin = User(email="admin@test.com", role="admin") clean_db.insert(admin) return admindef test_admin_access(admin_user): result = access_admin_page(admin_user) assert result.success테스트 코드가 의도만 남았다. 셋업 코드가 사라졌다. 가독성이 올라갔다. 유지보수도 쉬워졌다. 파라미터로 여러 케이스 일반 유저, 관리자, 게스트. 세 가지 권한 테스트가 필요했다. 처음엔 테스트를 세 개 만들려고 했다. 비효율적이다. @pytest.fixture(params=[ {"email": "user@test.com", "role": "user"}, {"email": "admin@test.com", "role": "admin"}, {"email": "guest@test.com", "role": "guest"} ]) def test_user(request, clean_db): user = User(**request.param) clean_db.insert(user) return userdef test_user_access(test_user): result = access_page(test_user) assert result.success테스트 하나가 자동으로 세 번 실행된다. 각각 다른 유저로. pytest 출력도 깔끔하다. test_user_access[user] PASSED test_user_access[admin] PASSED test_user_access[guest] PASSED코드는 한 번 작성. 케이스는 무한 확장.autouse로 자동 실행 로그 관리가 필요했다. 모든 테스트마다. @pytest.fixture(autouse=True) def setup_logging(): logger = setup_test_logger() yield logger.save_results()autouse=True. 명시 안 해도 자동으로 실행된다. 모든 테스트 함수에 로깅이 적용됐다. 코드 수정 없이. "이건 진짜 마법 같네." 실전 구조 conftest.py를 계층화했다. tests/ conftest.py # 전역 픽스처 api/ conftest.py # API 테스트용 test_user.py ui/ conftest.py # UI 테스트용 test_login.py전역 conftest.py: @pytest.fixture(scope="session") def db_connection(): # 디비 연결 pass@pytest.fixture def clean_db(db_connection): # 테이블 초기화 passapi/conftest.py: @pytest.fixture def api_client(): # API 클라이언트 pass@pytest.fixture def auth_header(api_client): # 인증 헤더 pass필요한 픽스처만 불러온다. 테스트가 가벼워졌다. 픽스처 조합 픽스처끼리 조합할 수 있다. @pytest.fixture def user(clean_db): user = User(email="test@test.com") clean_db.insert(user) return user@pytest.fixture def logged_in_user(user, api_client): token = api_client.login(user) user.token = token return user@pytest.fixture def user_with_data(logged_in_user, clean_db): data = create_test_data() clean_db.insert_for_user(logged_in_user, data) return logged_in_user세 단계 픽스처다. 로그인까지, 데이터까지. 선택 가능하다. def test_simple(user): # 유저만 필요 passdef test_with_login(logged_in_user): # 로그인된 유저 passdef test_full_scenario(user_with_data): # 전부 준비된 상태 pass필요한 만큼만 셋업한다. 시간 절약이다. 실제 성과 리팩토링 전후 비교했다. 전:테스트 50개 실행: 32분 셋업 코드 중복: 200줄 디비 연결: 50번 티어다운 누락: 가끔후:테스트 50개 실행: 8분 픽스처 재사용: 10개 디비 연결: 1번 티어다운: 자동24분 절약. 하루에 테스트 3번 돌린다. 72분 단축. 주 5일이면 6시간. 거의 야근 하루가 사라졌다. 팀원 반응 후배 QA한테 픽스처 가르쳤다. "이거 진짜 편하네요." 개발팀장도 만족했다. "CI 파이프라인이 30분 빨라졌어요." 테크 리드가 물었다. "다른 팀도 적용 가능할까요?" "conftest.py 공유하면 됩니다." 지금은 전사 표준이 됐다. 모든 테스트가 픽스처 기반이다. 주의할 점 픽스처 남용하지 말 것. 간단한 테스트에 복잡한 픽스처는 과하다. 테스트 3줄인데 픽스처 20줄이면 문제다. scope 실수 조심. session scope에 function 데이터 넣으면 망한다. 픽스처 이름 명확하게. data보다 user_test_data가 낫다. 의존성 순환 주의. 픽스처가 서로 부르면 안 된다. 다음 단계 factory_boy 도입 검토 중이다. 픽스처 + 팩토리 패턴. @pytest.fixture def user_factory(clean_db): return UserFactorydef test_multiple_users(user_factory): users = user_factory.create_batch(10) # 10명 유저로 테스트더 유연해진다. 랜덤 데이터도 가능하다. Faker 라이브러리도 붙이면 좋겠다. 실제 같은 데이터로. 지금 시작하기 픽스처 리팩토링 순서:중복 셋업 코드 찾기 (Ctrl+F "setup") conftest.py 만들고 기본 픽스처 작성 yield로 티어다운 자동화 scope 최적화 (function → class → module → session) 파라미터로 케이스 확장 팀 공유하루면 충분하다. 효과는 즉시 나타난다. 마무리 금요일 6시. 테스트 스위트 실행했다. 8분 만에 완료. 초록불. "퇴근 가능." 야근이 사라졌다. 픽스처 덕분이다. 테스트 코드도 코드다. 리팩토링이 필요하다. 반복을 제거하고 재사용하라. 시간은 돌아온다. 야근 대신 정시 퇴근으로.이제 pytest.ini 설정도 정리해야겠다. 커버리지 리포트 경로가 엉망이다.