LCOV - code coverage report
Current view: top level - src - bloc_test.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 31 31 100.0 %
Date: 2021-04-25 20:36:08 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : 
       3             : import 'package:bloc/bloc.dart';
       4             : import 'package:meta/meta.dart';
       5             : import 'package:test/test.dart' as test;
       6             : 
       7             : /// Creates a new `bloc`-specific test case with the given [description].
       8             : /// [blocTest] will handle asserting that the `bloc` emits the [expect]ed
       9             : /// states (in order) after [act] is executed.
      10             : /// [blocTest] also handles ensuring that no additional states are emitted
      11             : /// by closing the `bloc` stream before evaluating the [expect]ation.
      12             : ///
      13             : /// [build] should be used for all `bloc` initialization and preparation
      14             : /// and must return the `bloc` under test.
      15             : ///
      16             : /// [seed] is an optional `Function` that returns a state
      17             : /// which will be used to seed the `bloc` before [act] is called.
      18             : ///
      19             : /// [act] is an optional callback which will be invoked with the `bloc` under
      20             : /// test and should be used to interact with the `bloc`.
      21             : ///
      22             : /// [skip] is an optional `int` which can be used to skip any number of states.
      23             : /// [skip] defaults to 0.
      24             : ///
      25             : /// [wait] is an optional `Duration` which can be used to wait for
      26             : /// async operations within the `bloc` under test such as `debounceTime`.
      27             : ///
      28             : /// [expect] is an optional `Function` that returns a `Matcher` which the `bloc`
      29             : /// under test is expected to emit after [act] is executed.
      30             : ///
      31             : /// [verify] is an optional callback which is invoked after [expect]
      32             : /// and can be used for additional verification/assertions.
      33             : /// [verify] is called with the `bloc` returned by [build].
      34             : ///
      35             : /// [errors] is an optional `Function` that returns a `Matcher` which the `bloc`
      36             : /// under test is expected to throw after [act] is executed.
      37             : ///
      38             : /// ```dart
      39             : /// blocTest(
      40             : ///   'CounterBloc emits [1] when increment is added',
      41             : ///   build: () => CounterBloc(),
      42             : ///   act: (bloc) => bloc.add(CounterEvent.increment),
      43             : ///   expect: () => [1],
      44             : /// );
      45             : /// ```
      46             : ///
      47             : /// [blocTest] can optionally be used with a seeded state.
      48             : ///
      49             : /// ```dart
      50             : /// blocTest(
      51             : ///   'CounterBloc emits [10] when seeded with 9',
      52             : ///   build: () => CounterBloc(),
      53             : ///   seed: () => 9,
      54             : ///   act: (bloc) => bloc.add(CounterEvent.increment),
      55             : ///   expect: () => [10],
      56             : /// );
      57             : /// ```
      58             : ///
      59             : /// [blocTest] can also be used to [skip] any number of emitted states
      60             : /// before asserting against the expected states.
      61             : /// [skip] defaults to 0.
      62             : ///
      63             : /// ```dart
      64             : /// blocTest(
      65             : ///   'CounterBloc emits [2] when increment is added twice',
      66             : ///   build: () => CounterBloc(),
      67             : ///   act: (bloc) {
      68             : ///     bloc
      69             : ///       ..add(CounterEvent.increment)
      70             : ///       ..add(CounterEvent.increment);
      71             : ///   },
      72             : ///   skip: 1,
      73             : ///   expect: () => [2],
      74             : /// );
      75             : /// ```
      76             : ///
      77             : /// [blocTest] can also be used to wait for async operations
      78             : /// by optionally providing a `Duration` to [wait].
      79             : ///
      80             : /// ```dart
      81             : /// blocTest(
      82             : ///   'CounterBloc emits [1] when increment is added',
      83             : ///   build: () => CounterBloc(),
      84             : ///   act: (bloc) => bloc.add(CounterEvent.increment),
      85             : ///   wait: const Duration(milliseconds: 300),
      86             : ///   expect: () => [1],
      87             : /// );
      88             : /// ```
      89             : ///
      90             : /// [blocTest] can also be used to [verify] internal bloc functionality.
      91             : ///
      92             : /// ```dart
      93             : /// blocTest(
      94             : ///   'CounterBloc emits [1] when increment is added',
      95             : ///   build: () => CounterBloc(),
      96             : ///   act: (bloc) => bloc.add(CounterEvent.increment),
      97             : ///   expect: () => [1],
      98             : ///   verify: (_) {
      99             : ///     verify(() => repository.someMethod(any())).called(1);
     100             : ///   }
     101             : /// );
     102             : /// ```
     103             : ///
     104             : /// **Note:** when using [blocTest] with state classes which don't override
     105             : /// `==` and `hashCode` you can provide an `Iterable` of matchers instead of
     106             : /// explicit state instances.
     107             : ///
     108             : /// ```dart
     109             : /// blocTest(
     110             : ///  'emits [StateB] when EventB is added',
     111             : ///  build: () => MyBloc(),
     112             : ///  act: (bloc) => bloc.add(EventB()),
     113             : ///  expect: () => [isA<StateB>()],
     114             : /// );
     115             : /// ```
     116           1 : @isTest
     117             : void blocTest<B extends BlocBase<State>, State>(
     118             :   String description, {
     119             :   required B Function() build,
     120             :   State Function()? seed,
     121             :   Function(B bloc)? act,
     122             :   Duration? wait,
     123             :   int skip = 0,
     124             :   dynamic Function()? expect,
     125             :   Function(B bloc)? verify,
     126             :   dynamic Function()? errors,
     127             : }) {
     128           2 :   test.test(description, () async {
     129           2 :     await testBloc<B, State>(
     130             :       build: build,
     131             :       seed: seed,
     132             :       act: act,
     133             :       wait: wait,
     134             :       skip: skip,
     135             :       expect: expect,
     136             :       verify: verify,
     137             :       errors: errors,
     138             :     );
     139             :   });
     140             : }
     141             : 
     142             : /// Internal [blocTest] runner which is only visible for testing.
     143             : /// This should never be used directly -- please use [blocTest] instead.
     144             : @visibleForTesting
     145           1 : Future<void> testBloc<B extends BlocBase<State>, State>({
     146             :   required B Function() build,
     147             :   State Function()? seed,
     148             :   Function(B bloc)? act,
     149             :   Duration? wait,
     150             :   int skip = 0,
     151             :   dynamic Function()? expect,
     152             :   Function(B bloc)? verify,
     153             :   dynamic Function()? errors,
     154             : }) async {
     155           1 :   final unhandledErrors = <Object>[];
     156             :   var shallowEquality = false;
     157           2 :   await runZonedGuarded(
     158           1 :     () async {
     159           1 :       final states = <State>[];
     160           1 :       final bloc = build();
     161             :       // ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member
     162           2 :       if (seed != null) bloc.emit(seed());
     163           4 :       final subscription = bloc.stream.skip(skip).listen(states.add);
     164             :       try {
     165           2 :         await act?.call(bloc);
     166           1 :       } on Exception catch (error) {
     167           1 :         unhandledErrors.add(
     168           2 :           error is BlocUnhandledErrorException ? error.error : error,
     169             :         );
     170             :       }
     171           2 :       if (wait != null) await Future<void>.delayed(wait);
     172           2 :       await Future<void>.delayed(Duration.zero);
     173           2 :       await bloc.close();
     174             :       if (expect != null) {
     175           1 :         final dynamic expected = expect();
     176           2 :         shallowEquality = '$states' == '$expected';
     177           2 :         test.expect(states, test.wrapMatcher(expected));
     178             :       }
     179           2 :       await subscription.cancel();
     180           2 :       await verify?.call(bloc);
     181             :     },
     182           1 :     (Object error, _) {
     183           1 :       if (error is BlocUnhandledErrorException) {
     184           2 :         unhandledErrors.add(error.error);
     185           1 :       } else if (shallowEquality && error is test.TestFailure) {
     186             :         // ignore: only_throw_errors
     187           1 :         throw test.TestFailure(
     188           1 :           '''${error.message}
     189             : WARNING: Please ensure state instances extend Equatable, override == and hashCode, or implement Comparable.
     190           1 : Alternatively, consider using Matchers in the expect of the blocTest rather than concrete state instances.\n''',
     191             :         );
     192             :       } else {
     193             :         // ignore: only_throw_errors
     194             :         throw error;
     195             :       }
     196             :     },
     197             :   );
     198           3 :   if (errors != null) test.expect(unhandledErrors, test.wrapMatcher(errors()));
     199             : }

Generated by: LCOV version 1.15