flutter - 当 pumpWidget 为 ChangeNotifierProvider 时测试 Widget 抛出错误
问题描述
我正在通过这个codelab学习 Flutter Widget Test ,第一个示例抛出了一个我无法修复的错误:/,我正在尝试进行这个测试:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import '../lib/models/favorites.dart';
import '../lib/screens/home.dart';
Widget createHomeScreen() {
return ChangeNotifierProvider<Favorites>(
create: (context) => Favorites(),
child: MaterialApp(
home: HomePage(),
),
);
}
void main() {
group(
'Home Page Widget Tests',
() {
testWidgets(
'Testing Scrolling',
(tester) async {
await tester.pumpWidget(createHomeScreen());
expect(find.text('Item 0'), findsOneWidget);
await tester.fling(find.byType(ListView), Offset(0, -200), 3000);
await tester.pumpAndSettle();
expect(find.text('Item 0'), findsNothing);
},
);
},
);
}
home.dart 和 favorites.dart 代码:
主页.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:testing_app/models/favorites.dart';
import 'package:testing_app/screens/favorites.dart';
class HomePage extends StatelessWidget {
static String routeName = '/';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Testing Sample'),
actions: <Widget>[
TextButton.icon(
style: TextButton.styleFrom(primary: Colors.white),
onPressed: () {
Navigator.pushNamed(context, FavoritesPage.routeName);
},
icon: Icon(Icons.favorite_border),
label: Text('Favorites'),
),
],
),
body: ListView.builder(
itemCount: 100,
cacheExtent: 20.0,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => ItemTile(index),
),
);
}
}
class ItemTile extends StatelessWidget {
final int itemNo;
const ItemTile(
this.itemNo,
);
@override
Widget build(BuildContext context) {
var favoritesList = Provider.of<Favorites>(context);
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
),
title: Text(
'Item $itemNo',
key: Key('text_$itemNo'),
),
trailing: IconButton(
key: Key('icon_$itemNo'),
icon: favoritesList.items.contains(itemNo)
? Icon(Icons.favorite)
: Icon(Icons.favorite_border),
onPressed: () {
!favoritesList.items.contains(itemNo)
? favoritesList.add(itemNo)
: favoritesList.remove(itemNo);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(favoritesList.items.contains(itemNo)
? 'Added to favorites.'
: 'Removed from favorites.'),
duration: Duration(seconds: 1),
),
);
},
),
),
);
}
}
收藏夹.dart
import 'package:flutter/material.dart';
/// The [Favorites] class holds a list of favorite items saved by the user.
class Favorites extends ChangeNotifier {
final List<int> _favoriteItems = [];
List<int> get items => _favoriteItems;
void add(int itemNo) {
_favoriteItems.add(itemNo);
notifyListeners();
}
void remove(int itemNo) {
_favoriteItems.remove(itemNo);
notifyListeners();
}
}
出现的错误是:
00:03 +0: Home Page Widget Tests Testing Scrolling
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following ProviderNotFoundException was thrown building ItemTile(dirty):
Error: Could not find the correct Provider<Favorites> above this ItemTile Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that ItemTile is under your MultiProvider/Provider<Favorites>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using `builder` like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The relevant error-causing widget was:
ItemTile file:///home/avatar/Documents/Flutter%20projects/testing_app/lib/screens/home.dart:29:42
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:329:7)
#1 Provider.of (package:provider/src/provider.dart:281:30)
#2 ItemTile.build (file:///home/avatar/Documents/Flutter%20projects/testing_app/lib/screens/home.dart:44:34)
#3 StatelessElement.build (package:flutter/src/widgets/framework.dart:4569:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4495:15)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4189:5)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4474:5)
#7 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4469:5)
... Normal element mounting (33 frames)
#40 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3541:14)
#41 Element.updateChild (package:flutter/src/widgets/framework.dart:3306:18)
#42 SliverMultiBoxAdaptorElement.updateChild (package:flutter/src/widgets/sliver.dart:1229:37)
#43 SliverMultiBoxAdaptorElement.createChild.<anonymous closure> (package:flutter/src/widgets/sliver.dart:1214:20)
#44 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2647:19)
#45 SliverMultiBoxAdaptorElement.createChild (package:flutter/src/widgets/sliver.dart:1207:12)
#46 RenderSliverMultiBoxAdaptor._createOrObtainChild.<anonymous closure> (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:349:23)
#47 RenderObject.invokeLayoutCallback.<anonymous closure> (package:flutter/src/rendering/object.dart:1894:59)
#48 PipelineOwner._enableMutationsToDirtySubtrees (package:flutter/src/rendering/object.dart:915:15)
#49 RenderObject.invokeLayoutCallback (package:flutter/src/rendering/object.dart:1894:14)
#50 RenderSliverMultiBoxAdaptor._createOrObtainChild (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:338:5)
#51 RenderSliverMultiBoxAdaptor.addInitialChild (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:422:5)
#52 RenderSliverList.performLayout (package:flutter/src/rendering/sliver_list.dart:79:12)
#53 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#54 RenderSliverEdgeInsetsPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:137:12)
#55 RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:371:11)
#56 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#57 RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:512:13)
#58 RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1570:12)
#59 RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1479:20)
#60 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#61 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#62 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#63 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#64 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#65 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#66 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#67 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#68 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#69 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#70 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#71 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#72 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#73 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#74 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#75 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#76 RenderCustomPaint.performLayout (package:flutter/src/rendering/custom_paint.dart:546:11)
#77 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#78 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#79 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#80 MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:171:12)
#81 _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:925:7)
#82 MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:243:7)
#83 RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:407:14)
#84 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#85 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#86 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#87 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#88 _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1371:11)
#89 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#90 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#91 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#92 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#93 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#94 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#95 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#96 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#97 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#98 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#99 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#100 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#101 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#102 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#103 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#104 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#105 RenderOffstage.performLayout (package:flutter/src/rendering/proxy_box.dart:3362:13)
#106 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#107 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#108 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#109 _RenderTheatre.performLayout (package:flutter/src/widgets/overlay.dart:743:15)
#110 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#111 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#112 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#113 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#114 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#115 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#116 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#117 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#118 RenderCustomPaint.performLayout (package:flutter/src/rendering/custom_paint.dart:546:11)
#119 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#120 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#121 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#122 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:118:14)
#123 RenderObject.layout (package:flutter/src/rendering/object.dart:1784:7)
#124 RenderView.performLayout (package:flutter/src/rendering/view.dart:153:14)
#125 RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1641:7)
#126 PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:884:18)
#127 AutomatedTestWidgetsFlutterBinding.drawFrame (package:flutter_test/src/binding.dart:1105:23)
#128 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:319:5)
#129 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#130 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1082:9)
#131 AutomatedTestWidgetsFlutterBinding.pump.<anonymous closure> (package:flutter_test/src/binding.dart:969:9)
#134 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:72:41)
#135 AutomatedTestWidgetsFlutterBinding.pump (package:flutter_test/src/binding.dart:956:27)
#136 WidgetTester.pumpWidget.<anonymous closure> (package:flutter_test/src/widget_tester.dart:522:22)
#139 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:72:41)
#140 WidgetTester.pumpWidget (package:flutter_test/src/widget_tester.dart:519:27)
#141 main.<anonymous closure>.<anonymous closure> (file:///home/avatar/Documents/Flutter%20projects/testing_app/test/home_test.dart:23:24)
#142 main.<anonymous closure>.<anonymous closure> (file:///home/avatar/Documents/Flutter%20projects/testing_app/test/home_test.dart:22:9)
#143 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:144:29)
<asynchronous suspension>
<asynchronous suspension>
(elided 5 frames from dart:async and package:stack_trace)
════════════════════════════════════════════════════════════════════════════════════════════════════
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _TextFinder:<zero widgets with text "Item 0" (ignoring offstage widgets)>
Which: means none were found but one was expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure>.<anonymous closure> (file:///home/avatar/Documents/Flutter%20projects/testing_app/test/home_test.dart:25:11)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
...
This was caught by the test expectation on the following line:
file:///home/avatar/Documents/Flutter%20projects/testing_app/test/home_test.dart line 25
The test description was:
Testing Scrolling
════════════════════════════════════════════════════════════════════════════════════════════════════
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following message was thrown:
Multiple exceptions (2) were detected during the running of the current test, and at least one was
unexpected.
════════════════════════════════════════════════════════════════════════════════════════════════════
00:03 +0 -1: Home Page Widget Tests Testing Scrolling [E]
Test failed. See exception logs above.
The test description was: Testing Scrolling
00:03 +0 -1: Some tests failed.
我已经尝试在 MaterialAnd() 和 ChangueNotifierProvier() 周围放置一个 Builder() 但没有成功 TT(对不起,我的英语很糟糕)
解决方案
推荐阅读
- vb.net - ml.net 0.11 试图预测数组/数字序列 - 标签列“”的模式不匹配:预期 R4,得到向量
参数名称:labelCol' - ios - 不确定我是否应该使用实时数据库
- python - 如何检查值是否与python中的类型匹配?
- flutter - 如何在 Flutter 应用中保存登录名和用户信息
- vue.js - 带有子菜单的 Vuetify 导航抽屉
- mysql - MySQL如何在tsv文件中导入不同的字符集
- python - 与 PYod 的真正异常值有多大区别?
- php - Laravel 验证:只允许已知的属性/属性,否则验证失败
- python - Python 将 XML 解析为 CSV 编码问题
- excel - 工作表中的单元格以保护模式锁定,即使它们未标记为锁定