Skip to content

Commit f609a40

Browse files
authored
fix(bloc_test): test timeouts due to uncaught exceptions (#3854)
1 parent e0fdb60 commit f609a40

File tree

2 files changed

+38
-17
lines changed

2 files changed

+38
-17
lines changed

packages/bloc_test/lib/src/bloc_test.dart

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ Future<void> testBloc<B extends BlocBase<State>, State>({
196196
);
197197
Bloc.observer = testObserver;
198198

199-
await runZonedGuarded(
200-
() async {
199+
try {
200+
await _runZonedGuarded(() async {
201201
await setUp?.call();
202202
final states = <State>[];
203203
final bloc = build();
@@ -229,26 +229,36 @@ Future<void> testBloc<B extends BlocBase<State>, State>({
229229
await subscription.cancel();
230230
await verify?.call(bloc);
231231
await tearDown?.call();
232-
},
233-
(Object error, _) {
234-
if (shallowEquality && error is test.TestFailure) {
235-
// ignore: only_throw_errors
236-
throw test.TestFailure(
237-
'''
232+
});
233+
} catch (error) {
234+
if (shallowEquality && error is test.TestFailure) {
235+
// ignore: only_throw_errors
236+
throw test.TestFailure(
237+
'''
238238
${error.message}
239239
WARNING: Please ensure state instances extend Equatable, override == and hashCode, or implement Comparable.
240240
Alternatively, consider using Matchers in the expect of the blocTest rather than concrete state instances.\n''',
241-
);
242-
}
243-
if (errors == null || !unhandledErrors.contains(error)) {
244-
// ignore: only_throw_errors
245-
throw error;
246-
}
247-
},
248-
);
241+
);
242+
}
243+
if (errors == null || !unhandledErrors.contains(error)) {
244+
rethrow;
245+
}
246+
}
247+
249248
if (errors != null) test.expect(unhandledErrors, test.wrapMatcher(errors()));
250249
}
251250

251+
Future<void> _runZonedGuarded(Future<void> Function() body) {
252+
final completer = Completer<void>();
253+
runZonedGuarded(() async {
254+
await body();
255+
if (!completer.isCompleted) completer.complete();
256+
}, (error, stackTrace) {
257+
if (!completer.isCompleted) completer.completeError(error, stackTrace);
258+
});
259+
return completer.future;
260+
}
261+
252262
class _TestBlocObserver extends BlocObserver {
253263
const _TestBlocObserver(this._localObserver, this._onError);
254264

packages/bloc_test/test/bloc_bloc_test_test.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ void main() {
169169
});
170170
expect(actualError, exception);
171171
});
172+
173+
test('future still completes when uncaught exception occurs', () async {
174+
await expectLater(
175+
() => testBloc<ErrorCounterBloc, int>(
176+
build: () => ErrorCounterBloc(),
177+
act: (bloc) => bloc.add(CounterEvent.increment),
178+
expect: () => const <int>[1],
179+
),
180+
throwsA(isA<ErrorCounterBlocError>()),
181+
);
182+
});
172183
});
173184

174185
group('AsyncCounterBloc', () {
@@ -638,7 +649,7 @@ Alternatively, consider using Matchers in the expect of the blocTest rather than
638649
await completer.future;
639650
}, (Object error, _) {
640651
actualError = error;
641-
completer.complete();
652+
if (!completer.isCompleted) completer.complete();
642653
});
643654
expect((actualError as TestFailure).message, expectedError);
644655
});

0 commit comments

Comments
 (0)