Line data Source code
1 : import 'dart:async';
2 :
3 : import 'package:flutter/foundation.dart';
4 : import 'package:flutter/material.dart';
5 : import 'package:flutter/scheduler.dart';
6 : import 'package:flutter_test/flutter_test.dart' as flutter_test;
7 :
8 : /// 테스트 환경에서 사용할 유틸리티 함수들을 제공하는 클래스
9 : class TestUtils {
10 : /// 테스트 모드 강제 설정 (테스트에서 명시적으로 설정 가능)
11 : static bool _forceTestMode = false;
12 :
13 : /// 테스트 모드 강제 설정
14 12 : static void setTestMode(bool isTest) {
15 : _forceTestMode = isTest;
16 : }
17 :
18 : /// 현재 테스트 환경인지 확인 (개선된 버전)
19 15 : static bool get isInTest {
20 : // 강제 설정된 경우 해당 값 반환
21 : if (_forceTestMode) {
22 : return true;
23 : }
24 :
25 : // 다양한 방법으로 테스트 환경 감지 시도
26 : try {
27 : // 방법 1: flutter_test 라이브러리의 속성 확인
28 : return flutter_test.WidgetController.hitTestWarningShouldBeFatal;
29 : } catch (_) {
30 : try {
31 : // 방법 2: 테스트 환경에서만 존재하는 Zone 속성 확인
32 0 : return Zone.current['_testZoneSpecification'] != null;
33 : } catch (_) {
34 : try {
35 : // 방법 3: 디버그 모드 확인 (완벽한 방법은 아니지만 대안)
36 : return kDebugMode;
37 : } catch (_) {
38 : return false;
39 : }
40 : }
41 : }
42 : }
43 :
44 : /// 테스트용 고정 해시 생성 (테스트에서 예측 가능한 결과를 위해)
45 : static int _testHashCounter = 0;
46 :
47 : /// 테스트 환경에서 사용할 예측 가능한 해시 생성
48 1 : static String getTestHash() {
49 1 : _testHashCounter++;
50 2 : final counter = _testHashCounter.toString().padLeft(2, '0');
51 1 : return 'test$counter';
52 : }
53 :
54 : /// 현재 타이머가 활성화되어 있는지 확인
55 : /// 테스트 종료 시 !timersPending assertion 실패 원인 추적용
56 1 : static bool isTimerPending() {
57 : try {
58 : // flutter_test 라이브러리의 테스트 환경에서만 사용 가능
59 1 : if (isInTest) {
60 : // Zone 속성을 통해 타이머 상태 확인 시도
61 1 : final zone = Zone.current;
62 1 : if (zone['_timerCount'] != null) {
63 0 : return zone['_timerCount'] > 0;
64 : }
65 :
66 : // 다른 방법으로 타이머 상태 확인 시도
67 : try {
68 : // SchedulerBinding을 통해 프레임 스케줄링 상태 확인
69 1 : return SchedulerBinding.instance.hasScheduledFrame ||
70 0 : SchedulerBinding.instance.transientCallbackCount > 0;
71 : } catch (_) {
72 : // 실패 시 기본값 반환
73 : return false;
74 : }
75 : }
76 : } catch (e) {
77 : if (kDebugMode) {
78 0 : debugPrint('타이머 상태 확인 중 오류: $e');
79 : }
80 : }
81 :
82 : // 테스트 환경이 아니거나 오류 발생 시 기본값 반환
83 : return false;
84 : }
85 :
86 : /// 테스트 환경에서 사용할 예측 가능한 해시 생성 (테스트 파일과의 호환성을 위한 별칭)
87 1 : static String generatePredictableHash() {
88 1 : return getTestHash();
89 : }
90 :
91 : /// 테스트 환경에서 애니메이션을 비활성화하는 위젯
92 1 : static Widget disableAnimations({required Widget child}) {
93 1 : if (!isInTest) {
94 : return child;
95 : }
96 :
97 1 : return MediaQuery(
98 : data: const MediaQueryData(
99 : textScaleFactor: 1.0,
100 : platformBrightness: Brightness.light,
101 : padding: EdgeInsets.zero,
102 : viewInsets: EdgeInsets.zero,
103 : devicePixelRatio: 1.0,
104 : size: Size(800, 600),
105 : ),
106 1 : child: AnimatedBuilder(
107 : animation: const AlwaysStoppedAnimation<double>(1.0),
108 1 : builder: (BuildContext context, Widget? child) {
109 : return child!;
110 : },
111 : child: child,
112 : ),
113 : );
114 : }
115 :
116 : /// 테스트 환경에서 안전하게 SnackBar를 표시하는 함수
117 0 : static void showSnackBarSafely(BuildContext context, SnackBar snackBar) {
118 0 : if (!isInTest) {
119 0 : ScaffoldMessenger.of(context).showSnackBar(snackBar);
120 : return;
121 : }
122 :
123 : // 테스트 환경에서는 SnackBar 표시 대신 로그만 출력
124 0 : debugPrint('테스트 환경에서 SnackBar 표시 (내용: ${snackBar.content})');
125 : }
126 :
127 : /// 테스트 환경에서 안전하게 현재 SnackBar를 숨기는 함수
128 0 : static void hideCurrentSnackBarSafely(BuildContext context) {
129 0 : if (!isInTest) {
130 0 : ScaffoldMessenger.of(context).hideCurrentSnackBar();
131 : return;
132 : }
133 :
134 : // 테스트 환경에서는 로그만 출력
135 0 : debugPrint('테스트 환경에서 현재 SnackBar 숨김');
136 : }
137 : }
|