storeTest<T extends BaseStore> function

  1. @isTest
FutureOr<void> storeTest<T extends BaseStore>(
  1. String description, {
  2. required FutureOr<T> build(),
  3. dynamic act(
    1. T store
    )?,
  4. required dynamic expect(),
  5. Duration? wait,
  6. dynamic verify(
    1. T store
    )?,
})

Creates a new store-specific test case with the given description. storeTest will handle asserting that the store emits the expected states (in order) after act is executed. storeTest also handles ensuring that no additional states are emitted by closing the store stream before evaluating the expectation.

build should be used for all store initialization and preparation and must return the store under test.

act is an optional callback which will be invoked with the store under test and should be used to interact with the store.

expect is an optional Function that returns a Matcher which the store under test is expected to emit after act is executed.

verify is a callback which is invoked after expect and can be used for additional verification/assertions. verify is called with the store returned by build.

storeTest(
  'Counterstore emits [1] when update method is called',
  build: () => CounterStore(),
  act: (store) => store.update(1),
  expect: () => [1],
);

storeTest can also be used to wait for async operations by optionally providing a Duration to wait.

storeTest(
  'Counterstore emits [1] when increment is added',
  build: () => CounterStore(),
  act: (store) => store.update(1),
  wait: const Duration(milliseconds: 300),
  expect: () => [1],
);

[storeTest] can also be used to [verify] internal store functionality.

```dart
storeTest(
  'Counterstore emits [1] when update method is called',
  build: () => CounterStore(),
  act: (store) => store.update(1),
  expect: () => [1],
  verify: (_) {
    verify(() => repository.someMethod(any())).called(1);
  }
);

Implementation

@isTest
FutureOr<void> storeTest<T extends BaseStore>(
  String description, {
  required FutureOr<T> Function() build,
  Function(T store)? act,
  required Function() expect,
  Duration? wait,
  Function(T store)? verify,
}) async {
  test.test(description, () async {
    final completer = Completer();
    final store = await build();
    var i = 0;
    final _list = expect();
    final actualList = <String>[];
    Disposer disposer;
    final expectList = _list is List ? _list : List.from([_list]);
    var isFinished = false;
    await runZonedGuarded(() async {
      void testTriple(Triple triple, dynamic value) {
        if (isFinished) {
          return;
        }

        actualList.add(
          '''${triple.event.toString().replaceFirst('TripleEvent.', '')}($value)''',
        );

        if (i >= expectList.length) {
          throw test.TestFailure(
            '''Expected: $expectList Actual: $actualList''',
          );
        }

        final matcher = expectList[i];

        test.expect(matcher is TripleMatcher ? triple : value, matcher);
        i++;
        if (i >= expectList.length && !completer.isCompleted) {
          completer.complete(true);
        }
      }

      disposer = store.observer(
        onState: (value) => testTriple(store.triple, value),
        onError: (value) => testTriple(store.triple, value),
        onLoading: (value) => testTriple(store.triple, value),
      );
      act?.call(store);
      await Future.wait([
        completer.future,
        Future.delayed(wait ?? Duration.zero),
      ]);
      isFinished = true;
      await disposer.call();
      try {
        verify?.call(store);
      } on test.TestFailure catch (e) {
        throw VerifyError(e.message);
      }
    }, (Object error, _) {
      if (error is test.TestFailure) {
        throw test.TestFailure(
          '''Expected: $expectList
  Actual: $actualList

''',
        );
      } else {
        throw error;
      }
    });
  });
}