Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlocObserver testing capability proposal #4205

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions packages/bloc_test/lib/src/bloc_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ void blocTest<B extends BlocBase<State>, State>(
dynamic Function(B bloc)? verify,
dynamic Function()? errors,
FutureOr<void> Function()? tearDown,
BlocObserver? observer,
dynamic tags,
}) {
test.test(
Expand All @@ -164,6 +165,7 @@ void blocTest<B extends BlocBase<State>, State>(
verify: verify,
errors: errors,
tearDown: tearDown,
observer: observer,
);
},
tags: tags,
Expand All @@ -184,10 +186,12 @@ Future<void> testBloc<B extends BlocBase<State>, State>({
dynamic Function(B bloc)? verify,
dynamic Function()? errors,
FutureOr<void> Function()? tearDown,
BlocObserver? observer,
}) async {
var shallowEquality = false;
final unhandledErrors = <Object>[];
final localBlocObserver = Bloc.observer;
final firstObserver = Bloc.observer;
final localBlocObserver = observer ?? Bloc.observer;
final testObserver = _TestBlocObserver(
localBlocObserver,
unhandledErrors.add,
Expand All @@ -206,7 +210,9 @@ Future<void> testBloc<B extends BlocBase<State>, State>({
await act?.call(bloc);
} catch (error) {
if (errors == null) rethrow;
unhandledErrors.add(error);
if (!unhandledErrors.contains(error)) {
unhandledErrors.add(error);
}
}
if (wait != null) await Future<void>.delayed(wait);
await Future<void>.delayed(Duration.zero);
Expand Down Expand Up @@ -241,6 +247,8 @@ Alternatively, consider using Matchers in the expect of the blocTest rather than
if (errors == null || !unhandledErrors.contains(error)) {
rethrow;
}
} finally {
Bloc.observer = firstObserver;
}

if (errors != null) test.expect(unhandledErrors, test.wrapMatcher(errors()));
Expand Down Expand Up @@ -269,6 +277,37 @@ class _TestBlocObserver extends BlocObserver {
_onError(error);
super.onError(bloc, error, stackTrace);
}

@override
void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) {
_localObserver.onChange(bloc, change);
super.onChange(bloc, change);
}

@override
void onClose(BlocBase<dynamic> bloc) {
_localObserver.onClose(bloc);
super.onClose(bloc);
}

@override
void onCreate(BlocBase<dynamic> bloc) {
_localObserver.onCreate(bloc);
super.onCreate(bloc);
}

@override
void onEvent(Bloc<dynamic, dynamic> bloc, Object? event) {
_localObserver.onEvent(bloc, event);
super.onEvent(bloc, event);
}

@override
void onTransition(
Bloc<dynamic, dynamic> bloc, Transition<dynamic, dynamic> transition) {
_localObserver.onTransition(bloc, transition);
super.onTransition(bloc, transition);
}
}

String _diff({required dynamic expected, required dynamic actual}) {
Expand Down
27 changes: 27 additions & 0 deletions packages/bloc_test/test/bloc_bloc_test_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

import 'blocs/blocs.dart';
import 'blocs/failing_observer.dart';

class MockRepository extends Mock implements Repository {}

Expand Down Expand Up @@ -662,6 +663,32 @@ Alternatively, consider using Matchers in the expect of the blocTest rather than
});
});
});
group('BlocObservers', () {
test(
'FailingObserver',
() async {
Object? actualError;
final completer = Completer<void>();
await runZonedGuarded(() async {
unawaited(
testBloc<CounterBloc, int>(
build: () => CounterBloc(),
act: (bloc) => bloc.add(CounterEvent.increment),
errors: () => [isA<StateError>()],
observer: FailingObserver(),
).then((_) {
completer.complete();
}),
);
await completer.future;
}, (Object error, _) {
actualError = error;
if (!completer.isCompleted) completer.complete();
});
expect(actualError, isNull);
},
);
});

group('tearDown', () {
late int tearDownCallCount;
Expand Down
9 changes: 9 additions & 0 deletions packages/bloc_test/test/blocs/failing_observer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:bloc/bloc.dart';

class FailingObserver extends BlocObserver {
@override
void onEvent(Bloc<dynamic, dynamic> bloc, Object? event) {
super.onEvent(bloc, event);
throw StateError('catch me');
}
}