storeTest<T extends BaseStore> function
Creates a new store
-specific test case with the given description
.
storeTest will handle asserting that the store
emits the expect
ed
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 expect
ation.
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;
}
});
});
}