# 테스트 문제 해결 가이드 ## 목차 1. [소개](#소개) 2. [일반적인 테스트 실패 원인](#일반적인-테스트-실패-원인) 3. [EventBus 관련 문제](#eventbus-관련-문제) 4. [비동기 테스트 문제](#비동기-테스트-문제) 5. [메모리 및 리소스 관리 문제](#메모리-및-리소스-관리-문제) 6. [CI 환경 특화 문제](#ci-환경-특화-문제) 7. [문제 해결 도구](#문제-해결-도구) 8. [테스트 실행 가이드](#테스트-실행-가이드) ## 소개 이 문서는 볼링 매니저 앱의 테스트 실행 중 발생할 수 있는 다양한 문제와 그 해결 방법을 설명합니다. 특히 EventBus와 MemberService 테스트의 신뢰성 문제에 초점을 맞추고 있습니다. ## 일반적인 테스트 실패 원인 ### 1. 테스트 간 상태 격리 실패 **증상:** - 테스트를 개별적으로 실행하면 통과하지만, 전체 테스트를 실행하면 실패 - 테스트 실행 순서에 따라 결과가 달라짐 **원인:** - 테스트 간 공유 상태(싱글톤, 정적 변수 등)가 적절히 초기화되지 않음 - 테스트 종료 후 리소스가 제대로 정리되지 않음 **해결 방법:** - `setUp`과 `tearDown`에서 모든 공유 상태 초기화 - 싱글톤 인스턴스를 테스트마다 재설정 - `TestUtils.isInTest` 플래그를 활용하여 테스트 환경에서 특별한 처리 적용 - 격리 모드를 `strict`로 설정하여 테스트 간 상태 공유 최소화 ### 2. 타이밍 및 비동기 문제 **증상:** - 간헐적인 테스트 실패 - `Timeout` 오류 발생 - 비동기 작업 완료 전에 테스트가 종료됨 **원인:** - 고정된 지연 시간(`Future.delayed`)에 의존 - 비동기 작업 완료를 적절히 기다리지 않음 - 테스트 환경(CI vs 로컬)에 따른 실행 속도 차이 **해결 방법:** - 고정 지연 대신 `TestConfig`에서 환경별 설정 사용 - `expectLater`와 `completes` 매처 활용 - 비동기 작업 완료를 명시적으로 기다리는 코드 추가 ## EventBus 관련 문제 ### 1. 구독 해제 문제 **증상:** - `EventBus: 구독 취소됨` 로그가 누락됨 - 활성 구독 수(`_activeSubscriptionCount`)가 감소하지 않음 - 테스트 간 이벤트 간섭 발생 **원인:** - `dispose()` 메서드에서 구독 취소가 제대로 이루어지지 않음 - 구독 취소 전에 테스트가 종료됨 - 구독 카운터 관리 버그 **해결 방법:** - `dispose()` 메서드에서 모든 구독을 명시적으로 취소 - `tearDown`에서 `EventBus.resetAllTestInstances()` 호출 - 테스트 ID를 사용하여 테스트별 EventBus 인스턴스 격리 ### 2. 이벤트 전달 문제 **증상:** - 이벤트가 발생했지만 구독자에게 전달되지 않음 - 테스트에서 이벤트 처리 검증 실패 **원인:** - 이벤트 발생과 처리 사이의 비동기 타이밍 문제 - 잘못된 테스트 ID로 인한 이벤트 격리 - 이벤트 구독 전에 이벤트가 발생함 **해결 방법:** - 이벤트 발생 후 적절한 지연 추가 (환경 설정 기반) - 테스트 ID 설정 확인 - 이벤트 발생 전에 구독이 완료되었는지 확인 ## 비동기 테스트 문제 ### 1. 타임아웃 문제 **증상:** - `Test timed out after X milliseconds` 오류 - CI 환경에서 더 자주 발생 **원인:** - 테스트 타임아웃 값이 너무 짧음 - 비동기 작업이 예상보다 오래 걸림 - 리소스 경합으로 인한 지연 **해결 방법:** - `TestConfig`를 통해 환경별 타임아웃 값 조정 - 비동기 작업 완료를 명시적으로 기다림 - CI 환경에서 더 긴 타임아웃 설정 ### 2. 비동기 작업 완료 대기 문제 **증상:** - 비동기 작업 결과를 검증하기 전에 테스트가 종료됨 - 간헐적인 테스트 실패 **원인:** - `await`가 누락됨 - 잘못된 비동기 패턴 사용 - 이벤트 루프 블로킹 **해결 방법:** - 모든 비동기 작업에 `await` 사용 - `expectLater`와 함께 `completes` 또는 `completion` 매처 사용 - `pumpAndSettle`을 사용하여 모든 마이크로태스크가 완료될 때까지 대기 ## 메모리 및 리소스 관리 문제 ### 1. 리소스 누수 **증상:** - 메모리 사용량 증가 - 테스트 실행이 느려짐 - 장시간 테스트 실행 시 실패 증가 **원인:** - `dispose()` 메서드에서 리소스 정리 누락 - 구독, 스트림, 컨트롤러 등이 제대로 닫히지 않음 - 테스트 간 공유 리소스가 정리되지 않음 **해결 방법:** - 모든 서비스 클래스에 철저한 `dispose()` 구현 - `tearDown`에서 모든 리소스 정리 - 메모리 누수 감지 도구 활용 ### 2. 구독 관리 문제 **증상:** - `EventBus` 활성 구독 수가 계속 증가 - 이벤트가 예상보다 많은 구독자에게 전달됨 **원인:** - 구독 취소가 제대로 이루어지지 않음 - 구독 카운터 관리 버그 - 테스트 간 구독 객체 공유 **해결 방법:** - 모든 구독을 변수에 저장하고 명시적으로 취소 - `StreamSubscription` 객체 관리 개선 - 테스트 ID 기반 구독 격리 ## CI 환경 특화 문제 ### 1. 환경 차이로 인한 문제 **증상:** - 로컬에서는 통과하지만 CI에서는 실패하는 테스트 - CI에서 더 느린 테스트 실행 속도 - 리소스 제약으로 인한 타임아웃 **원인:** - CI 환경의 제한된 리소스 (CPU, 메모리) - 환경별 설정 차이 - 병렬 실행으로 인한 리소스 경합 **해결 방법:** - 환경 감지 및 설정 자동 조정 (`TestConfig` 활용) - CI 환경에서 더 긴 타임아웃 및 지연 설정 - 필요시 CI에서 순차 실행 적용 ### 2. 로깅 및 디버깅 문제 **증상:** - CI에서 테스트 실패 원인 파악이 어려움 - 로그 정보 부족 **원인:** - CI 환경에서 제한된 로그 출력 - 디버깅 정보 부족 **해결 방법:** - CI 환경에서 상세 로깅 활성화 - `TestDiagnostics` 유틸리티를 통한 자동 진단 정보 수집 - 실패 시 시스템 정보 수집 및 저장 ## 문제 해결 도구 ### 1. 테스트 진단 유틸리티 `TestDiagnostics` 클래스는 테스트 실패 시 자동으로 진단 정보를 수집하고 분석합니다: ```dart // 테스트 실패 기록 TestDiagnostics.instance.recordFailure( '테스트 이름', '오류 메시지', stackTrace: stackTrace ); // 테스트 종료 시 요약 생성 await TestDiagnostics.instance.generateSummary(); ``` ### 2. 테스트 재현 스크립트 테스트 실패를 재현하고 진단하기 위한 스크립트: ```bash # 특정 테스트 재현 ./scripts/reproduce_test.sh "MemberService 테스트" --verbose # 환경별 테스트 실행 ./scripts/run_tests.sh ci --pattern="EventBus" --timeout=3000 ``` ### 3. 테스트 설정 관리 환경별 테스트 설정을 관리하기 위한 `TestConfig` 클래스: ```dart // 환경별 설정 사용 final timeout = TestConfig.instance.asyncTimeout; final delay = TestConfig.instance.testDelayDuration; // 비동기 작업 대기 await Future.delayed(delay); ``` ## 테스트 실행 가이드 ### 로컬 환경에서 테스트 실행 ```bash # 전체 테스트 실행 ./scripts/run_tests.sh local # 특정 테스트만 실행 ./scripts/run_tests.sh local --pattern="MemberService" # 상세 로그와 함께 실행 ./scripts/run_tests.sh local --verbose ``` ### CI 환경에서 테스트 실행 ```bash # CI 환경 설정으로 테스트 실행 ./scripts/run_tests.sh ci # 순차 실행 (병렬 실행 비활성화) ./scripts/run_tests.sh ci --sequential # 타임아웃 증가 ./scripts/run_tests.sh ci --timeout=5000 ``` ### 테스트 실패 분석 ```bash # 실패한 테스트 재현 ./scripts/reproduce_test.sh "실패한 테스트 이름" # 진단 정보 수집 ./scripts/reproduce_test.sh "실패한 테스트 이름" --verbose ``` 테스트 진단 로그는 `test_diagnostics` 디렉토리에 저장되며, 실패 분석 및 재현 스크립트도 이 디렉토리에 생성됩니다.