Flutter应用进行自动化测试
前言
开发过程中,我们都会有一个很重要的环节,那就是测试。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: fluttertest: 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!的说明已经测试成功了。
测试模拟
测试输出
D:\project\dynamic_theme>flutter drive --target=test_driver/app.dart
Running "flutter pub get" in dynamic_theme... 1,399ms
Running Gradle task 'assembleDebug'...
Running Gradle task 'assembleDebug'... Done 28.3s
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Installing build\app\outputs\flutter-apk\app.apk... 803ms
00:00 +0: 切换导航滑动页面 (setUpAll)VMServiceFlutterDriver: Connecting to Flutter application at http://127.0.0.1:62607/4xhdHTUuf_U=/
VMServiceFlutterDriver: Isolate found with number: 3474149526286947
VMServiceFlutterDriver: Isolate is paused at start.
VMServiceFlutterDriver: Attempting to resume isolate
I/flutter ( 2384): debug版本--KK
VMServiceFlutterDriver: Connected to Flutter application.
00:02 +0: 切换导航滑动页面 切换页面I/flutter ( 2384): initial link:
I/flutter ( 2384): initialLink--
00:20 +1: 切换导航滑动页面 滑动页面到底部VMServiceFlutterDriver: waitFor message is taking a long time to complete...
00:39 +2: 切换导航滑动页面 滑动页面到顶部VMServiceFlutterDriver: waitFor message is taking a long time to complete...
00:52 +3: 切换导航滑动页面 跳转页面I/flutter ( 2384): 进入NewView
I/flutter ( 2384): _NewViewState#0c082(lifecycle state: initialized)
00:53 +4: 切换导航滑动页面 返回页面I/flutter ( 2384): 数据传参
00:58 +5: 切换导航滑动页面 (tearDownAll)00:58 +5: All tests passed! -
项目地址
到这里就已经结束,感兴趣的小伙伴可以去下载源码体验。如果不能运行请先检查flutter的版本。
最后: 大家可以去我公众号:伤心的辣条 ! 进去有许多资料共享!资料都是面试时面试官必问的知识点,也包括了很多测试行业常见知识,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。
好文推荐
转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!
面试经:一线城市搬砖!又面软件测试岗,5000就知足了…
面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…
什么样的人适合从事软件测试工作?
那个准点下班的人,比我先升职了…
测试岗反复跳槽,跳着跳着就跳没了…
Flutter应用进行自动化测试相关推荐
- dev c++自动补全_Flutter 自动化测试-开篇
什么是Flutter Flutter是Google旗下一款全新的跨平台UI开发框架,开发者可以通过同一套代码即可高效构建跨平台的应用,支持移动端.web端.桌面端,该项目是开源并免费的. 从2017年 ...
- Flutter技术与实战(6)
Flutter综合应用 文章目录 Flutter综合应用 线上出现问题,该如何做好异常捕获和信息采集 Flutter异常 App异常的捕获方式 FrameWork异常的捕获方式 异常上报 Dart接口 ...
- 入门前端学习路线图【送书】
大家好,我是若川.记得点上方音频听小姐姐配音,超级好听. 华章图书又赞助了书籍送福利给大家.本次送4本书的抽奖方式是:截止到9月6日(周一)20:00,在留言区留言任意内容.我会在留言区抽取「1位」关 ...
- 送你一份入门前端学习路线图
互联网时代,只要公司有开发互联网产品的需要,包括网站.网页.H5.小程序.APP等,就一定少不了前端开发工程师岗位.如今的"大前端时代",前端也已不限于传统PC端和移动手机端,随着 ...
- Flutter UI自动化测试技术方案选型与探索
Flutter页面无法直接使用Native测试工具定位元素,给自动化测试带来很多不便.虽然Google官方推出了Flutter driver 和 Integration test,但是在实际使用中存在 ...
- Flutter UI自动化测试技术方案选型与探索,初面蚂蚁金服
•不适用于混合栈APP,虽然appium中有相关的driver,但是无法切换环境.•元素定位能力相对薄弱.•依赖于VMService,需要构建Profile或Debug包. 基于以上因素,我们并没有直 ...
- 暴力突破 Flutter 自动化测试
一.前言 移动应用的测试往往比较复杂且工作量很大,为了验证用户的真实使用体验往往需要跨越多个平台以及不同的物理设备手动测试.随着产品功能不断迭代累积,测试的复杂度随之大幅增长,手动测试会变得更加困难. ...
- flutter和webapp_Flutter Web Beta版本终于发布了
在昨天的Flutter Interact大会中,谷歌Flutter团队给我们带来了最新的Flutter 1.12版本,在此次版本更新中,其中一个吸引人的功能就是"Flutter Web Be ...
- Flutter React编程范式实践
作者:闲鱼技术-匠修 Flutter Widget的设计灵感来源于React,是一款原生就立足于响应式的UI框架.本文基于Flutter特点,试图结合闲鱼在Flutter的工程应用来谈下我们对Flut ...
最新文章
- 《新一代人工智能伦理规范》发布
- JAVA连接数据库使用的API是什么呢,如何使用JDBC API在Java中建立数据库连接?
- 前驱、后驱和四驱,究竟哪个好?
- 关于方法论的对话之二敏捷与方法论
- 【Qt】设置应用程序图标
- @MapperScan和@ComponentScan使用问题
- ztree 标准得json数据格式_酷站推荐 - json-c.github.io/json-c - json-c API
- [MFC]关于Visual studio 2012的AfxGetMainWnd
- glibc静态链接 libc.a(nsswitch.o)(.data+0x64):undefined reference to `_nss_files_getaliasent_r' 错误解决方法
- 易语言_酷Q机器人插件_01
- 连接局域网中计算机与传输介质的网络设备是,连接局域网中的计算机与传输介质的网络连接设备是。...
- 一文读懂:完整的支付系统整体架构!
- 【天光学术】新闻学论文:校园网络流行语传播社会热点问题的途径(节选)
- Unity相机漫游脚本
- python使用selenium打开chrome浏览器时带用户登录信息
- 【java】javamail简介以及发送邮件
- 基于Java基础-面向对象实现植物大战僵尸简易版
- 在IIS7、IIS7.5中应用程序池最优配置方案
- Ajax跨域请求时出现Access to XMLHttpRequest at ‘xxx‘ from origin ‘xxx‘ has been been blocked by CORS policy
- 【WIN10】此用户无法登录,因为该账户当前已被禁用
热门文章
- python三角网格代码_python中shapely的多多边形三角网格/网格
- php数组堂each怎么保存变量,php – 数组和foreach循环可以更有效地处理输入变量...
- mysql 库名大小写_MySQL 库名、表名、字段名区分大小写吗???
- 小数的初步认识ppt_三年级数学知识点总结-10小数的初步认识
- c语言do while语句用法6,c语言do while的用法
- 中国石油计算机第三次在线作业,最新中国石油大学北京计算机应用基础第三次在线作业1(10页)-原创力文档...
- mysql hibernate 分页查询_hibernate分页查询的实现
- HALCON:Variation Model用法解析
- 蒙特卡洛粒子滤波定位算法_序列蒙特卡洛(SMC)与粒子滤波
- How to remove ROM in MAME