簡體   English   中英

Riverpod - 如何在測試中訪問提供者?

[英]Riverpod - How to access a provider in a test?

我有這個片段:

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++;
          },
        ),
      ],
    );
  }
}

這是一個非常簡化的代碼,但想法是它使用了 state 提供程序。

我想編寫一個測試並驗證,經過一些操作,提供程序位於特定的 state 中(不依賴於 UI,在這里我可以使用find.text() ,但我的 state 可能要復雜得多)。

在抽出我的小部件后,我想在我的測試中訪問 model:

await tester.pumpWidget(const CountWidget());

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

// ... Some other actions.

final currentCountState = // ?
expect(currentCountState, 3); // For example.

我怎樣才能做到這一點?

在這里, CountWidget擴展了ConsumerWidget ,后者擴展了ConsumerStatefulWidget ,后者又擴展了StatefulWidget

在 riverpod 的代碼中,我們可以看到創建的 state 實際上是一個_ConsumerState

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

  @override
  Widget build(BuildContext context) {
    return widget.build(context, ref);
  }
}

contextref實際上是相同的 object。

這是因為在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);
  }
}

關聯元素(用於context的元素是ConsumerStatefulElement

class ConsumerStatefulElement extends StatefulElement implements WidgetRef {
  // ...
}

所以在測試中,你可以使用tester.element來獲取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.

接受的答案有點 hacky,如果提供者在已被處置時被讀取,則會引發錯誤。 正確的方法是在測試中顯式使用ProviderContainerUncontrollerProviderScope 通過使用它,我們也可以在小部件之外使用提供程序。

// make sure riverpod is imported
import 'package:flutter_riverpod/flutter_riverpod.dart';

test('outside of widget', () {
   final container = ProviderContainer(
     overrides: [
       // define your overrides here if required
     ]
   );

   final count = container.read(countProvider);
   expect(count, 0);
});

testWidgets('Test the widget', () async {
   final container = ProviderContainer();

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

   await tester.tap(find.byIcon(Icons.add));
   final currentCount = container.read(countProvider);
   expect(currentCount, 1);
});

參考:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM