Riverpod - 如何在测试中访问提供商?

Val*_*nal 5 dart flutter flutter-test riverpod

我有这个片段:

final countProvider = StateProvider<int>((ref) {
  return 0;
});

class CountWidget extends ConsumerWidget {
  const CountWidget();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(countProvider);
    return Column(
      children: [
        Text(count.toString()),
        IconButton(
          icon: const Icon(Icons.add),
          onPressed: () {
            ref.read(countProvider.notifier).state++;
          },
        ),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常简化的代码,但其想法是它使用状态提供程序。

我想编写一个测试并验证在执行某些操作后,提供程序处于特定状态(不依赖 UI,这里我可以使用find.text(),但我的状态可能要复杂得多)。

我想在抽出我的小部件后在测试中访问模型:

await tester.pumpWidget(const CountWidget());

await tester.tap();
await tester.pump();

// ... Some other actions.

final currentCountState = // ?
expect(currentCountState, 3); // For example.
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Val*_*nal 7

解决方案1

ProviderScope有一个静态方法.containerOf,它返回当前的ProviderContainer更接近的。ProviderScopecontext

假设您想WidgetRef ref使用 key 获取小部件的关联/活动Key('key'),您可以使用 获取contexttester.element。然后你可以使用ProviderScope.containerOf

final context = tester.element(find.byType(Key('key')));
final providerContainter = ProviderScope.containerOf(context); // <- Your `ref`.
Run Code Online (Sandbox Code Playgroud)

解决方案2

在这里,CountWidget延伸ConsumerWidget延伸ConsumerStatefulWidget延伸StatefulWidget

在 Riverpod 的代码中,我们可以看到创建的状态实际上是一个_ConsumerState

class _ConsumerState extends ConsumerState<ConsumerWidget> {
  @override
  WidgetRef get ref => context as WidgetRef;

  @override
  Widget build(BuildContext context) {
    return widget.build(context, ref);
  }
}
Run Code Online (Sandbox Code Playgroud)

contextref实际上是同一个对象。

这是因为ConsumerStatefulWidget

/// A [StatefulWidget] that can read providers.
abstract class ConsumerStatefulWidget extends StatefulWidget {
  /// A [StatefulWidget] that can read providers.
  const ConsumerStatefulWidget({Key? key}) : super(key: key);

  @override
  // ignore: no_logic_in_create_state
  ConsumerState createState();

  @override
  ConsumerStatefulElement createElement() {
    return ConsumerStatefulElement(this);
  }
}
Run Code Online (Sandbox Code Playgroud)

关联元素(用于的元素contextConsumerStatefulElement

class ConsumerStatefulElement extends StatefulElement implements WidgetRef {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

所以在测试中,你可以使用tester.elementget ref

await tester.pumpWidget(const CountWidget());

await tester.tap();
await tester.pump();

// ... Some other actions.

final ref = tester.element<ConsumerStatefulElement>(find.byType(CountWidget));
final currentCountState = ref.read(countProvider);
expect(currentCountState, 3); // For example.
Run Code Online (Sandbox Code Playgroud)