CI codecov style license

rx_bloc_test

A Flutter package with the goal to enable testing RxBlocs from the FlutterRxBloc package with ease.

rxBlocTest

Testing a specific RxBloc state can be done easily with the rxBlocTest function. Besides a test description, it has two required parameters: build, which should return the testing RxBloc and state, which is a specific state inside the provided bloc.

Additionally, like in most cases, the state may return values which can be compared against other values. Here comes the expect parameter which takes a list of expected iterables and compares them with the ones from the state.

rxBlocTest<CounterBloc,int>(
  'Emits [] when created',
  build: () async => CounterBloc(), 
  state: (bloc) => bloc.states.count,
  expect: [],
)

There is an act parameter which is a callback executed after the bloc has been initialized. It should be used to add events to the block.

rxBlocTest<CounterBloc,int>(
  'Incrementing value',
  build: () async => CounterBloc(), 
  state: (bloc) => bloc.states.count,
  act: (bloc) async => bloc.events.increment(),
  expect: [1],
)

If you want, you can skip a custom amount of values that are emitted by the state by using the skip parameter. If no value is provided the default value of 1 is used, which will skip the initial value of the bloc state. The initial value can be included by setting the skip parameter to 0.

There is also a wait parameter which will wait for a given Duration to pass. This is useful especially when working with async operations.

rxBlocTest<DetailsBloc,String>(
  'Fetch third details data',
  build: () async => DetailsBloc(), 
  state: (bloc) => bloc.states.details,
  act: (bloc) async => bloc.events.fetch(),
  wait: Duration(milliseconds:100),
  skip: 3, // This will skip first two values + the initial value
  expect: ['Hello world'],
)

rxBlocFakeAsyncTest

For the most complex test cases with multiple events, which contain throttleTime or/and debounceTime or similar inside, and to speed up test execution,rxBlocFakeAsyncTest should be used. It contains an instance of FakeAsync inside act, which provides a way to fire all asynchronous events that are scheduled for that time period without actually needing the test to wait for real time to elapse.

Example

DetailsBloc email state mapper:

@override
Stream<String> _mapToEmailState() => _$setEmailEvent
  .startWith('')
  .throttleTime(const Duration(seconds: 3), trailing: true)
  .map((email) => email.trim())
  .shareReplay(maxSize: 1);

GOOD - execution time ±35 ms:

rxBlocFakeAsyncTest<DetailsBloc, String>(
  'Email fake async test bloc',
  build: () => DetailsBloc(repo),
  state: (bloc) => bloc.states.email,
  act: (bloc, fakeAsync) {
    bloc.events.setEmail(' job');
    bloc.events.setEmail(' job@prime');
    bloc.events.setEmail(' job@prime.com ');
    
    fakeAsync.elapse(const Duration(seconds: 3));
  },
  expect: <String>['', 'job@prime.com'],
);

BAD - execution time ±3 sec:

rxBlocTest<DetailsBloc, String>(
  'Email test bloc',
  build: () async => DetailsBloc(repo),
  state: (bloc) => bloc.states.email,
  act: (bloc) async {
    bloc.events.setEmail(' job');
    bloc.events.setEmail(' job@prime');
    bloc.events.setEmail(' job@prime.com ');  
  },
  expect: <String>['', 'job@prime.com'],
);

Libraries

rx_bloc_test