NOTE: The main, test and pubspec.yaml files are attached: sources.zip.
I have a simple BLoC called EnvBloc which yields new 'environments' whenever certain events happen. I also have another BLoC called CalculationBloc which can perform a calculation within a certain environment (the calculation may yield a few partial updates before finishing with a success). It listens to EnvBloc and if it discovers that the environment changed, it allows the current calculation to finish (which works by default to do asyncExpand in transformEvents), then resets itself and subsequently allows new calculation requests. The listening part is implemented like this in the constructor:
class CalculationBloc extends Bloc<CalculationEvent, CalculationState> {
CalculationBloc(EnvBloc envBloc) : super(CalculationInitial()) {
_envSubscription = envBloc.listen((envState) {
if (envState is EnvChangeSuccess) {
add(CalculationReset());
}
});
}
...
Now I would like to test this the following way:
CalculationBloc gets CalculationRequested and yields some states.EnvBloc sends it a EnvChangeSuccess state, and CalculationBloc should reset itself. This is implemented with a mixture of a mock and a StreamController (EnvBloc in this question's sources is trivial but in reality it isn't and I really need to use a mock instead of a real BLoC).CalculationRequested is sent and CalculationBloc yields some more states.I'm trying to do it using the blocTest helper, but I'm failing. The sequence in act is:
bloc.add(CalculationRequested());
envStreamController.sink.add(EnvChangeSuccess());
bloc.add(CalculationRequested());
but the state change in the env always comes last, i.e. my test behaves as if first two CalculationRequested events came, and only then the EnvChangeSuccess. I guess it has something to do with how Futures scheduling works in Dart, could you maybe explain what is going on if you know it?
I also tried to implement a test using the standard test function and await expectLater(...) but it also fails.
How can I test scenarios like this one?
Hi @wujek-srujek 馃憢
Thanks for opening an issue!
The issue is the EnvChangeSuccess is processed before the CalculationRequested event is added but the resulting CalculationReset event isn't added until after. You can address that by waiting until the EnvChangeSuccess has added the CalculationReset event and emitted a new state before adding the second CalculationRequested event:
blocTest<CalculationBloc, CalculationState>(
'calculation is reset when env changes and can be restarted',
build: () => calculationBloc,
act: (bloc) async {
bloc.add(CalculationRequested());
envStreamController.sink.add(EnvChangeSuccess());
await bloc.first;
bloc.add(CalculationRequested());
},
expect: [
CalculationUpdate(0),
CalculationUpdate(1),
CalculationUpdate(2),
CalculationSuccess(3),
// At this point, calculation is reset due to env change ...
CalculationInitial(),
// ... and subsequently it is restarted again due to a request.
CalculationUpdate(0),
CalculationUpdate(1),
CalculationUpdate(2),
CalculationSuccess(3),
],
);
In general, I would break this test up and just have one test which ensures that the state is reset in response to an environment change and another test to ensure that the CalculationRequested event yields the correct states.
Hope that helps 馃憤
Hi Felix, yes, I would break the test too, but it's hard - we are still at version 2.x.x of bloc and bloc_test (don't ask) and the seed parameter is not yet available there for blocTest, so it is hard to start the test at a certain state.
@wujek-srujek I completely understand. Let me know if there's anything else I can do to help 馃憤
I forgot - thank you very much for your help, yet again.