According to README.md
expectis an optionalIterable<State>which theblocunder test is expected to emit afteractis executed.
But sometimes actual states also contain states emitted because of the events in build
For example, here is the sample CounterBloc
import 'package:bloc/bloc.dart';
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.increment:
final newState = state + 1;
yield newState;
break;
case CounterEvent.decrement:
final newState = state - 1;
yield newState;
break;
}
}
}
And here is a test
blocTest(
"Emits [2] when CounterEvent.increment is added to bloc with state 1",
build: () async {
final bloc = CounterBloc();
bloc.add(CounterEvent.increment);
return bloc;
},
act: (bloc) async {
bloc.add(CounterEvent.increment);
},
expect: [2],
);
The above test behaves as expected and passes
Here is another test
blocTest(
"Emits [3] when CounterEvent.increment is added to bloc with state 2",
build: () async {
final bloc = CounterBloc();
bloc.add(CounterEvent.increment);
bloc.add(CounterEvent.increment);
return bloc;
},
act: (bloc) async {
bloc.add(CounterEvent.increment);
},
expect: [3],
);
This test fails with the following error
Expected: [3]
Actual: [2, 3]
Which: was <2> instead of <3> at location [0]
Why did not the second test pass?
If I want to test a bloc's response to an event when added to the bloc in StateX, how should I take my bloc to StateX? Should I do it by passing events in the build(the way I did in the above tests)? Is there a better way?
Hi @amanpalariya 👋
Thanks for opening an issue!
The second test fails because the bloc actually emits [2, 3] instead of just [3] which is expected behavior. You can currently modify your bloc to accept an initial state as an argument like:
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc(int initialState) : super(initialState ?? 0);
...
}
Then in your blocTest you could do:
blocTest(
'emits [3] when CounterEvent.increment is added to bloc with state 2',
build: () async => CounterBloc(2),
act: (bloc) async => bloc.add(CounterEvent.increment),
expect: [3],
)
Alternatively, you can leave your bloc implementation as is and use skip to only test the state you want:
blocTest(
'emits [3] when CounterEvent.increment is added to bloc with state 2',
build: () async => CounterBloc(),
act: (bloc) async => bloc..add(CounterEvent.increment)..add(CounterEvent.increment)..add(CounterEvent.increment),
skip: 3,
expect: [3],
)
In v7.0.0 (currently out as a dev release v7.0.0-dev.2) you will be able to seed your bloc state like so:
blocTest(
'emits [3] when increment is added and state is 2',
build: () => CounterBloc()..emit(2),
act: (bloc) => bloc.add(CounterEvent.increment),
expect: [3],
)
I hope that clears things up and I highly recommend upgrading to v7.0.0-dev (stable release coming very soon).
Closing for now but let me know if you have any other questions and I'm happy to continue the conversation 👍
Thank you for your quick reply 👍. I upgraded to v7.0.0-dev today. Really cool. Now, I feel confident in testing BLoCs.
Waiting for stable release... ❤
Most helpful comment
Thank you for your quick reply 👍. I upgraded to v7.0.0-dev today. Really cool. Now, I feel confident in testing BLoCs.
Waiting for stable release... ❤