TDD 작성
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_test/flutter_test.dart' as flutter_test;
|
||||
|
||||
/// 테스트 환경에서 사용할 유틸리티 함수들을 제공하는 클래스
|
||||
class TestUtils {
|
||||
/// 테스트 모드 강제 설정 (테스트에서 명시적으로 설정 가능)
|
||||
static bool _forceTestMode = false;
|
||||
|
||||
/// 테스트 모드 강제 설정
|
||||
static void setTestMode(bool isTest) {
|
||||
_forceTestMode = isTest;
|
||||
}
|
||||
|
||||
/// 현재 테스트 환경인지 확인 (개선된 버전)
|
||||
static bool get isInTest {
|
||||
// 강제 설정된 경우 해당 값 반환
|
||||
if (_forceTestMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 다양한 방법으로 테스트 환경 감지 시도
|
||||
try {
|
||||
// 방법 1: flutter_test 라이브러리의 속성 확인
|
||||
return flutter_test.WidgetController.hitTestWarningShouldBeFatal;
|
||||
} catch (_) {
|
||||
try {
|
||||
// 방법 2: 테스트 환경에서만 존재하는 Zone 속성 확인
|
||||
return Zone.current['_testZoneSpecification'] != null;
|
||||
} catch (_) {
|
||||
try {
|
||||
// 방법 3: 디버그 모드 확인 (완벽한 방법은 아니지만 대안)
|
||||
return kDebugMode;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 테스트용 고정 해시 생성 (테스트에서 예측 가능한 결과를 위해)
|
||||
static int _testHashCounter = 0;
|
||||
|
||||
/// 테스트 환경에서 사용할 예측 가능한 해시 생성
|
||||
static String getTestHash() {
|
||||
_testHashCounter++;
|
||||
final counter = _testHashCounter.toString().padLeft(2, '0');
|
||||
return 'test$counter';
|
||||
}
|
||||
|
||||
/// 현재 타이머가 활성화되어 있는지 확인
|
||||
/// 테스트 종료 시 !timersPending assertion 실패 원인 추적용
|
||||
static bool isTimerPending() {
|
||||
try {
|
||||
// flutter_test 라이브러리의 테스트 환경에서만 사용 가능
|
||||
if (isInTest) {
|
||||
// Zone 속성을 통해 타이머 상태 확인 시도
|
||||
final zone = Zone.current;
|
||||
if (zone['_timerCount'] != null) {
|
||||
return zone['_timerCount'] > 0;
|
||||
}
|
||||
|
||||
// 다른 방법으로 타이머 상태 확인 시도
|
||||
try {
|
||||
// SchedulerBinding을 통해 프레임 스케줄링 상태 확인
|
||||
return SchedulerBinding.instance.hasScheduledFrame ||
|
||||
SchedulerBinding.instance.transientCallbackCount > 0;
|
||||
} catch (_) {
|
||||
// 실패 시 기본값 반환
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
debugPrint('타이머 상태 확인 중 오류: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 테스트 환경이 아니거나 오류 발생 시 기본값 반환
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 테스트 환경에서 사용할 예측 가능한 해시 생성 (테스트 파일과의 호환성을 위한 별칭)
|
||||
static String generatePredictableHash() {
|
||||
return getTestHash();
|
||||
}
|
||||
|
||||
/// 테스트 환경에서 애니메이션을 비활성화하는 위젯
|
||||
static Widget disableAnimations({required Widget child}) {
|
||||
if (!isInTest) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
textScaleFactor: 1.0,
|
||||
platformBrightness: Brightness.light,
|
||||
padding: EdgeInsets.zero,
|
||||
viewInsets: EdgeInsets.zero,
|
||||
devicePixelRatio: 1.0,
|
||||
size: Size(800, 600),
|
||||
),
|
||||
child: AnimatedBuilder(
|
||||
animation: const AlwaysStoppedAnimation<double>(1.0),
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return child!;
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 테스트 환경에서 안전하게 SnackBar를 표시하는 함수
|
||||
static void showSnackBarSafely(BuildContext context, SnackBar snackBar) {
|
||||
if (!isInTest) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
return;
|
||||
}
|
||||
|
||||
// 테스트 환경에서는 SnackBar 표시 대신 로그만 출력
|
||||
debugPrint('테스트 환경에서 SnackBar 표시 (내용: ${snackBar.content})');
|
||||
}
|
||||
|
||||
/// 테스트 환경에서 안전하게 현재 SnackBar를 숨기는 함수
|
||||
static void hideCurrentSnackBarSafely(BuildContext context) {
|
||||
if (!isInTest) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
return;
|
||||
}
|
||||
|
||||
// 테스트 환경에서는 로그만 출력
|
||||
debugPrint('테스트 환경에서 현재 SnackBar 숨김');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user