Riverpod read method test remain in loading when widget test is finished

86 views Asked by At

I have this asyncProvider:

@riverpod
class NewUser extends _$NewUser {
  @override
  Future<UserModel?> build() {
    return getUser();
  }

  Future<UserModel?> getUser() async {
  ...return user;
  }
}

I mocked this provider like this:

class MockNewUserProvider extends AutoDisposeAsyncNotifier<UserModel?>
    with Mock
    implements NewUser {
  @override
  Future<UserModel?> build() {
    return getUser();
  }

  @override
  Future<UserModel?> getUser() async {
    return users.first;
  }

  @override
  AsyncValue<UserModel> get state {
    return AsyncData(userInfo.first);
  }
}

In the widget I am trying to write a test I am using read method inside build :

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var userState = ref.read(newUserProvider);

and I am using like this:

   const Divider(thickness: 2),
    Row(
      children: [
        userState.when( //---> loading
          data: (user) {

I debugged it and my mock provider is working but the issue is: userState is on loading and since I am using read it is not waiting to fetch data I guess If I use watch everything is ok but for read what I have to do?

this is my test:

void main() {
  late MockNewUserProvider mockMockNewUserProvider;

  setUp(() {
    mockMockNewUserProvider = MockNewUserProvider();
  });
  testWidgets('My widget loaded correctly', (tester) async {
    await tester.runAsync(() async {
      await tester.pumpApp(
          MyWidget(),
          overrides: [
            newUserProvider.overrideWith(() => mockMockNewUserProvider)
          ]);

      await tester.pumpAndSettle();

before changing in user state the test will be finished

1

There are 1 answers

2
Charles On

Generally speaking, you should mock the service/repository used in the Notifier/AsyncNotifier instead of mocking the Notifier/AsyncNotifier directly.

@riverpod
class NewUser extends _$NewUser {
  @override
  Future<UserModel?> build() {
     return ref.watch(userRepoProvider).getUser();
  }
  ... // Other methods
}

// Mock
class MockUserRepo extends Mock implements UserRepo {
  @override
  Future<UserModel?> getUser() async {
    return UserModel.empty;
  }
}

final container = ProviderContainer(
  overrides: [
    userRepoProvider.overrideWith(
      (ref) => MockUserRepo(),
    ),
  ]
);

If you really need to mock the generated Notifier/AsyncNotifier. The Mock class needs to extend _$NewUser and it means that the Mock class have to be in the same file of the Notifier/AsyncNotifier.

class MockNewUser extends _$NewUser with Mock implements NewUser {}

For more details, check out the official docs here