Files

138 lines
4.4 KiB
Dart

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(
textScaler: TextScaler.linear(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 숨김');
}
}