【Flutter】自动测试探索

        根据flutter官方文档的说明,flutter可以对我们的应用,进行自动化测试,保证我们应用的稳定性和功能的完整性,并且可以快速修复问题。

自动化测试可分为以下几类:

单元测试:测试单一的函数,方法或类

组件测试:测试单一的 widget

集成测试:测试一个完整的应用或者一个应用的大部分功能。

        我的整个探索,也是根据这上面3项项进行的。

官方文档地址:

测试 Flutter 应用 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter学习不同类型的测试以及如何编写它们。https://flutter.cn/docs/testing

一.单元测试

        测试单一的函数,方法或类。单元测试的目标是验证逻辑单元在各种条件下的正确性。因为,在很多对日项目,都在追求单元测试覆盖率的前提下,推荐以一个物理文件为单位,编写一组测试。整个过程如下:

        step1.新建一个Flutter 项目 fluttertest并编写一个逻辑处理类lib/counter.dart

class Counter {
  int value = 0;

  void increment() => value++;

  void decrement() => value--;
}

        step2.项目追加test依赖

 flutter pub add test

         step3.创建一个单元测试类,在test目录中 counter_test.dart

import 'package:counter_app/counter.dart';
import 'package:test/test.dart';

void main() {
  group('Counter', () {
    test('value should start at 0', () {
      expect(Counter().value, 0);
    });

    test('value should be incremented', () {
      final counter = Counter();

      counter.increment();

      expect(counter.value, 1);
    });

    test('value should be decremented', () {
      final counter = Counter();

      counter.decrement();

      expect(counter.value, -1);
    });
  });
}

        这样单元测试就编写好了,单元测试主要测试的是逻辑处理单元,用到了test作为一个测试单元,在其中进行相关的测试逻辑编写,并使用expect进行期望判定,同时,可以通过group对test进行分类。

        step4.通过命令,可以运行单元测试,并获取相应的测试报告。

flutter test --coverage 

        step5.执行上述的测试后,我们可以在项目根目录中找到coverage文件夹,在文件夹中已经为我们生成好了lcov.info文件,该文件中计入了测试的覆盖率信息,我们可以通过vscode 上的插件对他进行简单的查看。VSCode LCOV - Visual Studio Marketplace

        当然,我们还需要对这个文件进行转换,生成相应的测试报告。这个转换过程在windows下,我们需要搭建Linux环境,并通过相应的指令进行。环境搭建需要依次安装下面的内容。

MinGW-W64->msys2->lcov 相关的软件我都是在SourceForge上找到并安装。搜关键字即可。
Compare, Download & Develop Open Source & Business Software - SourceForgeSourceForge provides free & fast open source software downloads and development, and business software reviews and comparisons featuring the largest open source and business software directory.https://sourceforge.net/Linux Test Project - Coverage » lcov (sourceforge.net)icon-default.png?t=N6B9https://ltp.sourceforge.net/coverage/lcov.php

安装的简单说明:不会的不明白的可以自己百度,这个没什么难度。不浪费篇幅,贴图了。

  • MinGW-W64 和msys2都只下一步就可以了。
  • lcov 是解压文件,需要解压,并把lcov、gendesc、genhtml、geninfo、genpng复制到mingw64的bin目录下。然后在吧整个bin目录加到系统的环境变量中path中。
  • 找到msys2的shell入口cmd文件C:\msys64\msys2_shell.cmd,修改该文件。检索set MSYS2_PATH_TYPE=inherit 把该行的rem去掉,让其在cmd运行时执行,以便把我们的windows系统环境变量,加入到msys2中。

        经过上面的安装,我们再msys2的shell环境中,进入到我们的项目目录,如果你的项目在e盘,你需要通过cd /e/ 来进入。

当你进入到的项目根目录后,你需要执行下面的指令,来完成转换

genhtml coverage/lcov.info -o coverage/html

Administrator@BAC1901170-PC MSYS /e/test/fluttertest
$ genhtml coverage/lcov.info -o coverage/html
Reading data file coverage/lcov.info
Resolved relative source file path "lib\counter.dart" with CWD to "/e/test/fluttertest/lib/counter.dart".
Found 2 entries.
Found common filename prefix "/e/test/fluttertest"
Writing .css and .png files.
Generating output.
Processing file lib/main.dart
Processing file lib/counter.dart
Writing directory view page.
Overall coverage rate:
  lines......: 92.3% (24 of 26 lines)
  functions..: no data found

然后再到自己的根目录下,就可以看到html文件夹,双击html/index.html 就可以看到报告了。

总结:

上述内容容易理解,记住一下二点即可:

1.flutter单元测试主要用到 test expect group 三个函数

2.理解到单元测试的主要目标是,测试逻辑的正确性

另外:覆盖率是度量测试完整性的一个手段,是测试有效性的一个度量

测试覆盖是对测试完全程度的评测。覆盖率_百度百科

2.组件测试

        组件测试,就是widget测试,对widget类的测试,这部分的测试弥补了单元测试只能测试业务逻辑而不能测试画面组件的缺憾。从而提高我们整个测试的覆盖率。

        当你新建一个flutter项目时,创建模板就已经将组件测试包含到你的项目中了,你无需再添加flutter_test依赖,就可以使用他了。

dev_dependencies:
  flutter_test:
    sdk: flutter

        当然你也将在test文件夹中,找到widget_test文件,在这个文件里已经编写好了一个典型的widget测试。我们一起学习一下他的构造。

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:fluttertest/main.dart';

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(const MyApp());

    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

testWidgets测试方法,这个方法中定义了我们整个widget测试的内容。

WidgetTester提供的 pumpWidget方法会建立并渲染我们提供的 widget。在调用pumpwidget后,我们还可以调用pump等函数用于重新构建widget,这个对StatefulWidget 或者动画会非常有用。

在一个expect函数中,我们可以通过expct(Finder,Matcher)来验证,控件的内容期望。

案例是通过find.text这个Finder,来查找指定文本内容的组件,通过findsOneWidget 这个Matcher来进行判断

常用的Matchers:

findsOneWidget                  验证widget 只在屏幕中出现一次

findsNothing                        验证没有可被查找的 widgets。

findsWidgets                       验证一个或多个 widgets 被找到。

findsNWidgets                     验证特定数量的 widgets 被找到。

matchesGoldenFile             验证渲染的 widget 是否与特定的图像匹配(「目标文件」测试)。

常用的finder构造函数:

find.text 它会创建一个 Finder 来寻找显示特定文本 String 的 widget

find.byKey 通过已经提供给 widget 的 Key 来查找 widget

如果上述示例不适用于一些特殊情况,请到 CommonFinders 文档 中查看更多用法

WidgetTester 提供了文本输入、点击、拖动的相关方法:

在很多情况下,用户交互会更新应用状态。在测试环境中,Flutter 在状态发生改变的时候并不会自动重建 widget。为了保证模拟用户交互实现后,widget 树能重建,一定要调用 WidgetTester 提供的 pump() 或 pumpAndSettle()

3.集成测试

        Unit tests 和 Widget tests 并不能够测试单独的模块形成的整体或者获取真实设备上应用运行状态。这些任务需要集成测试 (integration tests) 来处理。

        他能够达到的效果是在真机上运行测试脚本,进行自动化测试。正体编写与单元测试和组件测试类似。具体步骤如下:

        step1.引入依赖

dev_dependencies:
  integration_test:
    sdk: flutter
  flutter_test:
    sdk: flutter

        step2.项目根目录下创建integration_test 文件夹,编写一个app_test.dart的文件,内容如下

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:integration_test/integration_test.dart';


void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('end-to-end test', () {
    testWidgets('Main Test', (WidgetTester tester) async {
      //进入主程序
      await tester.pumpWidget(MyApp());
      //等待加载一会
      await Future.delayed(const Duration(seconds: 3));
      //找到对应的入力框
      Finder userFinder = find.byType(TextFormField).at(0);
      Finder passwordFinder = find.byType(TextFormField).at(1);
      expect(userFinder, findsOneWidget);
      expect(passwordFinder, findsOneWidget);
      //填写用户名密码
      await tester.enterText(userFinder, 'xxxx');
      await tester.enterText(passwordFinder, 'xxxx');
      await tester.pumpAndSettle();
      
      //等待3秒后执行点击登录按钮操作
      await Future.delayed(const Duration(seconds: 3)).then((value) async {
        Finder loginFinder = find.byType(LoginButton);
        expect(loginFinder, findsOneWidget);
        await tester.tap(loginFinder);
        //需要等待动画加载画面迁移(此处不能省略)
        await tester.pumpAndSettle();
        await Future.delayed(const Duration(seconds: 10));
      });


    });
  });
}

注意:await tester.pumpAndSettle();的调用,在用动画的画面,迁移画面时必须调用,否则看不到相应动画的测试与调用。

        step3.启动设备,运行测试 

flutter test integration_test --coverage

        step4.查看报告(参2.组件测试)


其他测试相关内容,有待探索。。。

《TODO》处理滚动

处理滚动 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter如何在 widget 测试中处理滚动。https://flutter.cn/docs/cookbook/testing/widget/scrolling

《TODO》模拟(Mocks)允许我们仿造一个线上服务或数据库使用 Mockito 模拟依赖关系 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter使用 Mockito package 在测试中模拟服务端行为。https://flutter.cn/docs/cookbook/testing/unit/mocking

《TODO》为 plugin 编写单元测试如何测试 Flutter Pluginhttps://flutter.cn/docs/development/packages-and-plugins/plugins-in-tests

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值