Flutter应用进行自动化测试

2154 篇文章 119 订阅
1312 篇文章 21 订阅

在这里插入图片描述

前言

开发过程中,我们都会有一个很重要的环节,那就是测试。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!的说明已经测试成功了。

测试模拟

在这里插入图片描述

测试输出

在这里插入图片描述

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就知足了…

面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…

什么样的人适合从事软件测试工作?

那个准点下班的人,比我先升职了…

测试岗反复跳槽,跳着跳着就跳没了…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值