Reputation: 85
Describe the bug
I have a StateNotifierProvider which is (cardOnboardingProvider), that holds CardTypesState. when I initialize this provider in the constructor function, I make an API call, and on ".then( () => {})" I set the state of that provider.
When I want to test it. There is another provider (onboardingProvider) and trigger this provider and make it initialize.
After running the test, it shows an error and says this
===== asynchronous gap ===========================
And also :
MissingStubError: 'getCardTypesList' No stub was found which matches the arguments of this method call: getCardTypesList('', {isMock: true})
which is not an issue because I've removed the asynchronous API call from to provider and I saw this one resolved as well.
This works fine for me in the app. It's just the testing that shows an error.
To Reproduce
** Provider **
@freezed
class CardTypesState with _$CardTypesState {
const factory CardTypesState.idle() = _CardTypesIdle;
const factory CardTypesState.showError() = _CardTypeShowError;
const factory CardTypesState.loading() = _CardTypesLoading;
const factory CardTypesState.showCardList({required List<CardTypesModel> cardList}) = _CardTypeShowCardList;
}
final cardOnboardingProvider = StateNotifierProvider<CardOnboardingNotifier, CardTypesState>((ref) {
return CardOnboardingNotifier(ref: ref, filteringApiCalls: ref.watch(cardTypesListApiCallsProvider));
});
class CardOnboardingNotifier extends StateNotifier<CardTypesState> {
Ref ref;
final CardTypeListApiCalls filteringApiCalls;
CardOnboardingNotifier({required this.ref, required this.filteringApiCalls}) : super(CardTypesState.loading()) {
getListTypes();
}
void getListTypes() {
state = CardTypesState.loading();
filteringApiCalls.getCardTypesList().then((value) {
state = CardTypesState.showCardList(cardList: value);
});
}
}
** Test file **
Future<List<ListTypeResponse>> listTypesMockFromFile() {
final listTypeResponse = File('sample/sample/sample/sample.json').readAsStringSync();
final listTypeJson = json.decode(listTypeResponse);
final List<ListTypeResponse> listTypes =
listTypeJson.map<ListTypeResponse>((i) => ListTypeResponse.fromJson(i as Map<String, dynamic>)).toList();
return Future.value(listTypes);
}
@GenerateMocks([FacadeStoreService, FacadeHttpRestClient])
void main() {
group("Group test", () {
final happyPath = ProviderContainer(
overrides: [
storeProvider.overrideWith((ref) => MockFacadeStoreService()),
httpClientProvider.overrideWith((ref) => MockFacadeHttpRestClient()),
],
);
test("Initial value", () async {
/// Check onboardingProvider state
expect(happyPath.read(onboardingProvider), OnboardingState.showVehicleType(isNextAvailable: false));
expect(happyPath.read(onboardingProvider.notifier).pageIndex, 0);
// ATTENTION : the above provider(onboardingProvider) is triggering (cardOnboardingProvider) which is
// The provider that makes the asynchronous API call.
/// Check CardOnboardingProvider state
final Future<List<ListTypeResponse>> futureListTypes = listTypesMockFromFile();
when(happyPath.read(httpClientProvider).getCardTypesList("", {"isMock": "true"})).thenAnswer((_) => futureListTypes);
expect(happyPath.read(httpClientProvider).getCardTypesList("", {"isMock": "true"}), same(futureListTypes));
});
});
}
Expected behavior We need to make an API call and set the value on the initialization of the provider. How come it can't just initiate the provider and then change the value after the asynchronous function?
Upvotes: 1
Views: 72