-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
docs: testing initial events #4229
Comments
For unit tests I would say it should not matter which first even you are adding to a bloc. After all, you should treat it as adding whichever event at whichever time. You can test that in widget tests of i.e. your page, that the correct event is added. I would say mock the Bloc and check whether it is triggered. |
Out of curiosity, do you typically write (widget) tests for initial Bloc events?
Would you mind sharing a sample of what that would look like? |
@LukasMirbt good question. When I write widget tests, I would rather test what happends in what state. But you can for sure also test which event you have called when providing it, aka the first event. It is probably a usefull test since it tests that you didnt change the event etc. which might potentially break something. But I have never written such a test tbo :D Something like this? writing it out of memorry in here, so it might not work 100 %, so take it rather as a concept: late final MyBloc mockedBloc;
setUpAll(() {
mockedBloc = MockMyBloc();
when(mockedBloc.close).thenAnswer((_) => Future.value());
// To make the page work, you have to provide some state that bloc builders would take.
final state = SomeState();
whenListen(
mockedBloc,
Stream.fromIterable([state]),
initialState: state,
);
});
patrolWidgetTest(
'when state is AuthLoginStateFailure',
($) async {
// arrange
// pumpApp is extension method, imagine $.pumpWidget with MaterialApp etc.
await $.pumpApp(const MyPage(myBloc: mockedBloc)); // pass the bloc, or mock get_it if you use that or whatever
// act
// assert
verify(() => mockedBloc.add(MyFirstEvent())).called(1);
},
); imagine that you in that MyPage() you have something like this, BlocProvider(
create: (context) => myBloc..add(MyFirstEvent()),
child: MyWidget(),
); |
Interesting!
Something like that might work but there are some subtle issues;
|
void mockInLocator<T extends Object>(T mock) {
when(
() => GetIt.I.get<T>(
param1: any<dynamic>(named: 'param1'),
param2: any<dynamic>(named: 'param2'),
),
).thenReturn(mock);
when(
() => GetIt.I.call<T>(
param1: any<dynamic>(named: 'param1'),
param2: any<dynamic>(named: 'param2'),
),
).thenReturn(mock);
}
// for example, in set up
mockInLocator<MyBloc>(mockedBloc);
|
I understand, thanks for taking the time to create a sample 👍 |
Hi @LukasMirbt 👋 Generally, I agree with @tenhobi's suggestions to test the initial event is added in your widget tests since this functionality of part of the widget tree (not the bloc itself). An example of such a test can be found in the flutter_todos example. This is also something that would be caught by adding integration tests to your flutter app. |
I agree, that sounds very reasonable 👍
This widget test seems to verify repository behavior. I would prefer to avoid that since this would couple the widget test to domain layer details. It would be great to be able to test the interface to the Bloc directly, similar to this. Let me know what you think! |
Something similar to the class MockCounterBloc extends MockBloc<CounterEvent, CounterState>
implements CounterBloc {}
void main() {
group(CounterPage, () {
late CounterBloc counterBloc;
setUp(() {
counterBloc = MockCounterBloc();
when(() => counterBloc.state).thenReturn(CounterState());
});
Widget buildSubject() => MaterialApp(home: CounterPage());
testWidgets('adds $CounterStarted when $CounterBloc is created',
(tester) async {
await tester.pumpWidget(buildSubject());
final blocProvider = tester.widget<TestableBlocProvider<CounterBloc>>(
find.byType(TestableBlocProvider<CounterBloc>),
);
blocProvider.onCreated(counterBloc);
verify(() => counterBloc.add(CounterStarted())).called(1);
});
}); class TestableBlocProvider<T extends StateStreamableSource<Object?>>
extends StatelessWidget {
const TestableBlocProvider({
required this.create,
required this.onCreated,
required this.child,
super.key,
});
final T Function(BuildContext context) create;
final void Function(T bloc) onCreated;
final Widget child;
@override
Widget build(BuildContext context) {
return BlocProvider<T>(
create: (context) {
final bloc = create(context);
onCreated(bloc);
return bloc;
},
child: child,
);
}
} |
Description
Hi!
In #3944, it's mentioned that the recommended way to add initial events to a Bloc is as follows:
The reasoning being that
However, this creates a new problem. How do you test that the correct initial events are added when the Bloc is created?
As far as I know, none of the Bloc examples or documentation demonstrate a way to do this.
Is adding the initial events not considered worth testing?
Related issues: #3946, #3944, #3759, #2701, #2654, #1912, #1415
The text was updated successfully, but these errors were encountered: