简体   繁体   中英

Flutter: How can I unit test a Timer used by a Bloc using blocTest?

How can I test a Timer inside a Bloc, using blocTest ? I'm using the bloc library , with freezed to build the state and event objects (might not matter here, but who knows).

So let's say I have something like this in my bloc class:

@override
Stream<MyState> mapEventToState(
    MyEvent event,
    ) {
  return event.when(
    start: (value) async* {
      yield state.copyWith(/* add state data */);
    },
    getStream: _myStream(),
  );
}

Stream<MyState> _myStream() async* {
  MyResult? result;

  try {
    final repo = await _repoProvider();
    result = await repo.fetchResult();
  } on Exception catch (e) {
    _logger.e(e);
    /* do some stuff irrelevant to the example */
  }
  
  Timer(const Duration(minutes: 1), () {
      add(const MyEvent.getStream());

  });

  yield state.copyWith(
  /* fill state object with data*/
  );
}

So if my bloc receives a getStream event, the _myStream() function is called to handle the emitting. This function starts a timer, to submit another getStream event after 1 minute. How can I test this in a unit test without having to wait for a minute (I'm using bloc library's bloc_test to write blocTest functions for my blocs. This comes with a wait functionality, but it really just waits for the submitted amount of time)? I've tried to solve this using FakeAsync, but to no avail - I could always only register the first event. I thought something like this would work, but it doesn't: blocTest<MyBloc, MyState>( "repo should be called twice", build: () {

    return TravelBloc(
     
      mockRepoProvider,
      mockTrackerProvider,
    );
  },
  act: (bloc) =>
      fakeAsync((async) {
        bloc.add(const MyEvent.getStream());
        async.elapse(Duration(minutes: 1, seconds: 1));
      }),
  expect: () => [
       /* check state here */
      ],
  verify: (_) {
    verify(mockRepo.fetchResult).called(2);
  });

Is there any good solution how to test such a construction properly without actual waiting? fakeAsync seems to be the correct choice but I see myself unable to combine it with blocTest .

As you mentioned wait parameter from the blocTest method should be used for testing the things like timers, debounces, etc. However, in your case, this wouldn't be sufficient, because your unit test(s) become too slow to execute.

You have just to apply the DI principle (by the way, you are already using it when you supply mocks to the BLoC) and provide the Duration object to your TravelBloc 's constructor. So your build callback from blocTest will look like this:

blocTest(
...
build: () => TravelBloc(
       mockRepoProvider,
       mockTrackerProvider,
       updateInterval: tUpdateInterval,
),
...
);

This technique is very helpful when you need to "control" something inside your class/method within your test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM