dart - 在图像小部件上测试 GestureDetector
问题描述
我制作了一个简单的测试用例应用程序,您可以在其中单击一个小部件,使用GestureDetector
该小部件触发对变量的setState
更新tapCount
。
该应用程序正在模拟器中运行,文本更新正确,如上所示,但是一旦我尝试 Flutter 小部件测试,小部件测试就会失败,因为文本在测试环境中没有正确更新。
可重现的例子:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp();
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int tapCount = 0;
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
MyImage(
onTap: () {
setState(() {
tapCount += 1;
});
},
imagePath: 'assets/my-image.jpg',
),
Text(tapCount.toString())
],
),
),
),
);
}
}
class MyImage extends StatelessWidget {
final Function() onTap;
final String imagePath;
const MyImage({
Key key,
@required this.onTap,
@required this.imagePath,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
this.onTap();
},
child: Image.asset(
imagePath,
height: 100.0,
),
);
}
}
在 pubspec 中,我下载了一个随机图像并验证该图像成功显示在模拟器中。
assets:
- assets/my-image.jpg
await tester.pumpAndSettle();
我的测试与添加并点击图像的示例相同:
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
await tester.pumpAndSettle();
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the image and trigger a frame.
await tester.tap(find.byType(MyImage));
await tester.pump();
await tester.pumpAndSettle();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing); // this test fails
expect(find.text('1'), findsOneWidget); // this test fails
});
}
当我运行测试时出现此错误
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: no matching nodes in the widget tree
Actual: ?:<exactly one widget with text "0" (ignoring offstage widgets): Text("0")>
Which: means one was found but none were expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure> (file:///Projects/untitled/test/widget_test.dart:27:5)
<asynchronous suspension>
#5 testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:82:23)
#6 TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:566:19)
<asynchronous suspension>
#9 TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:550:14)
#10 AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:893:24)
#16 AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:890:15)
#17 testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:81:22)
#18 Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:27)
<asynchronous suspension>
#19 Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:249:15)
<asynchronous suspension>
#24 Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:246:5)
#25 Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:166:33)
#30 Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:165:13)
<asynchronous suspension>
#31 Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:399:25)
<asynchronous suspension>
#45 _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
#46 _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
#47 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
(elided 28 frames from class _FakeAsync, package dart:async, and package stack_trace)
This was caught by the test expectation on the following line:
file:///Projects/untitled/test/widget_test.dart line 27
The test description was:
Counter increments smoke test
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: Counter increments smoke test
如果我尝试相同的测试,但将Image
内部MyImage
替换为内部的另一个小部件(例如另一个Text
小部件)main.dart
,则测试通过:
class MyImage extends StatelessWidget {
final Function() onTap;
final String imagePath;
const MyImage({
Key key,
@required this.onTap,
@required this.imagePath,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
this.onTap();
},
child: Text( // replaced Image with Text and test passes!
imagePath,
),
);
}
}
这让我认为问题是由于使用了图像,但我不知道为什么。
如果您想尝试测试,代码也会上传到GitHub 。
解决方案
这是我对为什么它不适用于图像的看法。颤振测试在 FakeAsync 区域中运行,当您需要运行真正的异步代码(例如通过assetBundle 加载资产)时,资产不会被加载并且图像小部件的大小保持为零,因此命中测试失败。如果您事先设置图像的高度和宽度,则测试通过。
推荐阅读
- git - git refspec 中的 + 是什么?
- voice-recognition - Nao Webots 模拟器的语音命令
- javascript - 检索订阅条带 node.js 的状态
- python - 使用函数中的while循环查找给定列表中第一次出现的负数
- spring-cloud-stream - Spring cloud stream rabbit binder:向DLQ发送批处理消息时出错
- typescript - 错误 TS2416:“ApolloError”类型中的属性“originalError”不可分配给基类型“GraphQLError”中的相同属性
- powerbi - 在报告发布到 Microsoft Teams 时监控 Power BI 报告的使用/访问
- sql-server - 如何在一个事务中执行多个 Invoke-Sqlcmd?
- c - 我将数组的旧元素保存在新数组中。我该如何解决这个问题?
- javascript - 如何调试 Outlook 加载项?