前言
开发过程中,我们都会有一个很重要的环节,那就是测试。Flutter开发也一样,我们当我们完成了应用的开发之后,需要对我们的软件进行测试。市面上也有很多可以用于测试的一些自动化的软件。在这里介绍一下flutter自带的测试,我们可以通过这个插件,对我们的整个应用进行自动化的测试。
运行环境
[√] Flutter (Channel stable, 2.2.3, on Microsoft Windows [Version 10.0.19042.1110], locale zh-CN)
• Flutter version 2.2.3 at D:\flutter
• Framework revision f4abaa0735 (6 weeks ago), 2021-07-01 12:46:11 -0700
• Engine revision 241c87ad80
• Dart version 2.13.4
• Pub download mirror https://pub.flutter-io.cn
• Flutter download mirror https://storage.flutter-io.cn
安装依赖
首先,我们需要安装flutter_driver
插件。安装完成以后,我们在项目中新建一个文件夹(我的文件名是test_driver
自己定义文件夹名字)。这个文件夹就用于我们的测试代码的编写。
dev_dependencies:
flutter_driver:
sdk: flutter
test: any
代码编写
我要写的一个测试代码,就是我要通过代码去找到我需要的组件,比如按钮,我需要去点击它。滑动列表我需要往下划动到第多少页。然后我再往回划,切换到下一个页面,如此循环的。按照我们人类思维的流程,把整个项目全部模拟操作。
app.dart
测试入口文件
import 'package:flutter_driver/driver_extension.dart';
import 'package:dynamic_theme/main.dart' as app;
void main() {
// This line enables the extension.
enableFlutterDriverExtension();
// Call the `main()` function of the app, or call `runApp` with
// any widget you are interested in testing.
app.main();
}
app_test.dart
执行需要测试模块
我写了entrance
的文件模块,里面包含了页面点击滚动的模拟事件,如果需要全模块的测试可以在main
函数加多个group
需要测试某个功能也可以按需测试。
import 'package:test/test.dart';
import 'entrance.dart';
void main() {
group('切换导航滑动页面', entrance);
}
entrance.dart
模拟测试脚本
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void entrance() {
late FlutterDriver driver;
// Connect to the Flutter driver before running any tests.
setUpAll(() async {
driver = await FlutterDriver.connect();
});
// Close the connection to the driver after the tests have completed.
tearDownAll(() async {
await driver.close();
});
// test('starts at 0', () async {
// Use the `driver.getText` method to verify the counter starts at 0.
// expect(await driver.getText(counterTextFinder), '0');
// });
test('切换页面', () async {
await Future.delayed(Duration(seconds: 2));
await driver.tap(find.byValueKey('tab_3'));
await Future.delayed(Duration(seconds: 5));
await driver.tap(find.byValueKey('tab_2'));
await Future.delayed(Duration(seconds: 5));
await driver.tap(find.byValueKey('tab_1'));
await Future.delayed(Duration(seconds: 5));
await driver.tap(find.byValueKey('tab_0'));
});
test('滑动页面到底部', () async {
await driver.runUnsynchronized(() async {
final listFinder = find.byValueKey('message_list');
final itemFinder = find.byValueKey('item_78');
await driver.scrollUntilVisible(
listFinder,
itemFinder,
dyScroll: -300.0,
);
});
});
test('滑动页面到顶部', () async {
await driver.runUnsynchronized(() async {
final listFinder = find.byValueKey('message_list');
final itemFinder = find.byValueKey('item_1');
await driver.scrollUntilVisible(
listFinder,
itemFinder,
dyScroll: 300.0,
);
});
});
test('跳转页面', () async {
// First, tap the button.
await driver.tap(find.byValueKey('jump_list'));
// Then, verify the counter text is incremented by 1.
expect(await driver.getText(find.byValueKey('title')), 'NewList-路由传参');
});
test('返回页面', () async {
await Future.delayed(Duration(seconds: 5));
final buttonFinder = find.byValueKey('back');
// First, tap the button.
await driver.tap(buttonFinder);
// Then, verify the counter text is incremented by 1.
// expect(await driver.getText(counterTextFinder), '1');
});
}
怎样找到我们需要的组件?
测试脚本使用find.byValueKey('key的名称')
来找到我们需要的组件,找到以后我们可以进行点击、滚动、双击等模拟操作。这里我使用的是find.byValueKey
方法,下面介绍它的使用。
找到滚动列表,滚动某个位置
首先我们要找到我们需要滑动的列表,我find.byValueKey
这个方法去找到滑动组件,在开始写业务代码的时候我已经加了一个key
名称是message_list
。key
不要重复避免查找组件出现问题。
业务代码
EasyRefresh.custom(
key: Key('message_list'),
enableControlFinishRefresh: true,
...
测试代码例子
driver.scrollUntilVisible
模拟滑动,itemFinder
参数表示滑动到key:item_78
这个元素的时候就不滑动了,接下来每次滑动300像素。
test('滑动页面到底部', () async {
await driver.runUnsynchronized(() async {
final listFinder = find.byValueKey('message_list');
final itemFinder = find.byValueKey('item_78');
await driver.scrollUntilVisible(
listFinder,
itemFinder,
dyScroll: -300.0,
);
});
});
driver
支持的方法
- [checkHealth](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/checkHealth.html)({[Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[Health](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/Health-class.html)>
- Checks the status of the Flutter Driver extension.
- [clearTimeline](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/clearTimeline.html)({[Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout: _kUnusuallyLongTimeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Clears all timeline events recorded up until now. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/clearTimeline.html)
- [close](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/close.html)() → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Closes the underlying connection to the VM service. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/close.html)
- [enterText](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/enterText.html)([String](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/String-class.html) text, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Enters `text` into the currently focused text input, such as the [EditableText](https://docs-flutter-io.firebaseapp.com/flutter/widgets/EditableText-class.html) widget. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/enterText.html)
- [forceGC](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/forceGC.html)() → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Force a garbage collection run in the VM.
- [getRenderTree](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getRenderTree.html)({[Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[RenderTree](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/RenderTree-class.html)>
- Returns a dump of the render tree.
- [getSemanticsId](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getSemanticsId.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[int](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/int-class.html)>
- Retrieves the semantics node id for the object returned by `finder`, or the nearest ancestor with a semantics node. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getSemanticsId.html)
- [getText](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getText.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[String](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/String-class.html)>
- Returns the text in the `Text` widget located by `finder`.
- [getVmFlags](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getVmFlags.html)() → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[List](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/List-class.html)<[Map](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Map-class.html)<[String](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/String-class.html), dynamic>>>
- Returns the Flags set in the Dart VM as JSON. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/getVmFlags.html)
- [requestData](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/requestData.html)([String](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/String-class.html) message, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[String](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/String-class.html)>
- Sends a string and returns a string. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/requestData.html)
- [runUnsynchronized](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/runUnsynchronized.html)<T>([Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<T> action(), { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<T>
- `action` will be executed with the frame sync mechanism disabled. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/runUnsynchronized.html)
- [screenshot](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/screenshot.html)() → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[List](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/List-class.html)<[int](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/int-class.html)>>
- Take a screenshot. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/screenshot.html)
- [scroll](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scroll.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) dx, [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) dy, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) duration, { [int](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/int-class.html) frequency: 60, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Tell the driver to perform a scrolling action. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scroll.html)
- [scrollIntoView](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollIntoView.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) alignment: 0.0, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Scrolls the Scrollable ancestor of the widget located by `finder` until the widget is completely visible. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollIntoView.html)
- [scrollUntilVisible](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollUntilVisible.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) scrollable, [SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) item, { [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) alignment: 0.0, [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) dxScroll: 0.0, [double](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/double-class.html) dyScroll: 0.0, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Repeatedly [scroll](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scroll.html) the widget located by `scrollable` by `dxScroll` and `dyScroll` until `item` is visible, and then use [scrollIntoView](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollIntoView.html) to ensure the item's final position matches `alignment`. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/scrollUntilVisible.html)
- [setSemantics](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setSemantics.html)([bool](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/bool-class.html) enabled, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[bool](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/bool-class.html)>
- Turns semantics on or off in the Flutter app under test. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setSemantics.html)
- [setTextEntryEmulation](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setTextEntryEmulation.html)({[bool](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/bool-class.html) enabled, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Configures text entry emulation. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/setTextEntryEmulation.html)
- [startTracing](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/startTracing.html)({[List](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/List-class.html)<[TimelineStream](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/TimelineStream-class.html)> streams: _defaultStreams, [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout: _kUnusuallyLongTimeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Starts recording performance traces. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/startTracing.html)
- [stopTracingAndDownloadTimeline](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/stopTracingAndDownloadTimeline.html)({[Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout: _kUnusuallyLongTimeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[Timeline](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/Timeline-class.html)>
- Stops recording performance traces and downloads the timeline. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/stopTracingAndDownloadTimeline.html)
- [tap](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/tap.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Taps at the center of the widget located by `finder`.
- [traceAction](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/traceAction.html)([Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html) action(), { [List](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/List-class.html)<[TimelineStream](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/TimelineStream-class.html)> streams: _defaultStreams, [bool](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/bool-class.html) retainPriorEvents: false }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<[Timeline](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/Timeline-class.html)>
- Runs `action` and outputs a performance trace for it. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/traceAction.html)
- [waitFor](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitFor.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Waits until `finder` locates the target.
- [waitForAbsent](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitForAbsent.html)([SerializableFinder](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/SerializableFinder-class.html) finder, { [Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Waits until `finder` can no longer locate the target.
- [waitUntilNoTransientCallbacks](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitUntilNoTransientCallbacks.html)({[Duration](https://docs-flutter-io.firebaseapp.com/flutter/dart-core/Duration-class.html) timeout }) → [Future](https://docs-flutter-io.firebaseapp.com/flutter/dart-async/Future-class.html)<void>
- Waits until there are no more transient callbacks in the queue. [[...]](https://docs-flutter-io.firebaseapp.com/flutter/flutter_driver/FlutterDriver/waitUntilNoTransientCallbacks.html)
find
方法支持的查找方式find.text
find.byValueKey
find.bySemanticsLabel
find.pageBack
find.byType
SerializableFinder text(String text) => ByText(text);
/// Finds widgets by [key]. Only [String] and [int] values can be used.
SerializableFinder byValueKey(dynamic key) => ByValueKey(key);
/// Finds widgets with a tooltip with the given [message].
SerializableFinder byTooltip(String message) => ByTooltipMessage(message);
/// Finds widgets with the given semantics [label].
SerializableFinder bySemanticsLabel(Pattern label) => BySemanticsLabel(label);
/// Finds widgets whose class name matches the given string.
SerializableFinder byType(String type) => ByType(type);
/// Finds the back button on a Material or Cupertino page's scaffold.
SerializableFinder pageBack() => const PageBack();
run flutter drive --target=test_driver/app.dart
现在我们已经写好了自动化测试脚本run flutter drive --target=test_driver/app.dart
命令,会在控制台上面看到很多输出的信息,说明已经开始自动化测试了,最后我们会看到控制台输出了All tests passed!
的说明已经测试成功了。
测试模拟
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!