Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provider container loses data in widget test #3529

Closed
Colman opened this issue May 8, 2024 · 4 comments
Closed

Provider container loses data in widget test #3529

Colman opened this issue May 8, 2024 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@Colman
Copy link

Colman commented May 8, 2024

Describe the bug
Provider state is lost during widget test even though it's keep alive

To Reproduce
Test code:

void main() {
  testWidgets('Some test', (WidgetTester tester) async {
    final container = ProviderContainer();
    addTearDown(container.dispose); //This line doesn't change the result

    final subscription = container.listen(
      testProvider,
      (_, __) {},
      fireImmediately: true,
    );
    container.read(testProvider.notifier).updateWith(7);

    print('Inside test');
    print(container.read(testProvider).value);

    await tester.pumpWidget(
      UncontrolledProviderScope(
        container: container,
        child: Bug(),
      ),
    );

    subscription.close();
  });
}

TestWidget

class TestWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final value = ref.watch(testProvider).value;
    print('Build');
    print(value);
    return Container();
  }
}

TestProvider

@Riverpod(keepAlive: true)
class Test extends _$Test {
  @override
  Future<int> build() async {
    return 5;
  }

  void updateWith(int value) {
    state = AsyncData(value);
  }
}

This will print the following:

Inside test
7
Build
5

Expected behavior
Should print:

Inside test
7
Build
7

How are we supposed to initialize AsyncNotifiers for tests? It's discouraged to mock notifiers according to the docs. I tried it anyway and got errors that I couldn't figure out how to fix. Now I'm trying to set the values of the notifiers before running the tests and Flutter clears them for some reason.

I thought maybe it had something to do with being outside of the pump widget function, so I tried passing a callback to the TestWidget that gets called inside of a postFrameCallback in its initState. In that callback, it would initialize these notifiers for me. This works, but as soon as the widget builds again, the values are cleared.

@Colman Colman added bug Something isn't working needs triage labels May 8, 2024
@rrousselGit
Copy link
Owner

What's the "bug" widget?

@rrousselGit rrousselGit added question Further information is requested and removed needs triage labels May 8, 2024
@Colman
Copy link
Author

Colman commented May 8, 2024

What's the "bug" widget?

Sorry I meant TestWidget. I've just updated it.

@rrousselGit rrousselGit removed the question Further information is requested label May 13, 2024
@rrousselGit
Copy link
Owner

Your TestNotifier.build is to blame.

It is marked as async, which makes the initialisation, well, async. So you're effectively setting the state to 7 before it is initialised to 5.
Essentially you're setting the state during AsyncLoading.

Remove the async and you're good to go.

@Riverpod(keepAlive: true)
class Test extends _$Test {
  @override
  FutureOr<int> build() {
    return 5;
  }

  void updateWith(int value) {
    state = AsyncData(value);
  }
}

@rrousselGit
Copy link
Owner

An alternative is to await for the state to be initialized before modifying it.
You can do:

    await container.read(testProvider.future);
    container.read(testProvider.notifier).updateWith(7);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants