Flutter 學習之路 - 測試(單元測試,Widget 測試,集成測試)

Flutter 學習之路 - 測試(單元測試,Widget 測試,集成測試)

實驗 Flutter 的三種測試方法--- 代碼Github地址html

關於 Flutter 的測試

前段時間去紐約的 Google 參加 Flutter 的聚會,聽到在 Google Material Flutter 團隊的 MH Johnson 在臺上講 Flutter 的測試,想到本身該學習了哈哈哈。git

通常來講,通過良好測試的應用應該有不少 unit tests 和 widget test,經過代碼覆蓋率(code coverage)進行跟蹤,以及須要足夠的集成測試來涵蓋全部重要的使用場景。下面的表格,總結了在不一樣類型測試的特色,方便在選擇的時候進行權衡:github

單元測試 Widget 測試 集成測試
置信度 Low Higher Highest
維護成本 Low Higher Highest
依賴 Few More Lots
執行速度 Quick Slower Slowest

那麼這三個重要程度是怎麼樣呢?這個圖能夠參考一下:api

單元測試

參考文章(主要就是按這個學習翻譯的,英文 ok 能夠直接看官網):linkbash

測試單一功能、方法或類。例如,被測單元的外部依賴性一般被模擬出來,如package:mockito。 單元測試一般不會讀取/寫入磁盤、渲染到屏幕,也不會從運行測試的進程外部接收用戶操做。單元測試的目標是在各類條件下驗證邏輯單元的正確性。app

第一步:添加 test 或者 flutter_test 依賴

pubspec.yaml 裏添加以下方法(嫌麻煩能夠把冒號以後寫 any)less

dev_dependencies:
  test: <latest_version>
複製代碼

加上之後記得按一下 Packages getasync

第二步:建立測試文件

目錄結構以下:(測試文件寫在 test 文件裏面)ide

flutter_road_test/
  lib/
    counter.dart
  test/
    counter_test.dart
複製代碼
第三步:建立要測試的類

建立一個要被測試的單元,這個單元能夠是一個方法或一個類,下面在 lib/counter.dart 文件中建立 Counter 類:單元測試

class Counter {
  int value = 0;

  void increment() => value++;

  void decrement() => value--;
}
複製代碼
第四步:爲這個類寫 test

testexpect 都來自 test 這個包:

// Import the test package and Counter class
import 'package:test/test.dart';
import 'package:counter_app/counter.dart';

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

    counter.increment();

    expect(counter.value, 1);
  });
}
複製代碼
第五步:若是有多個測試,能夠用 group

group 裏面包含了三個測試(初始狀態測試,increment 方法測試,counter.decrement 方法測試)

import 'package:test/test.dart';
import 'package:counter_app/counter.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);
    });
  });
}
複製代碼
第六步:運行測試

在命令行下運行:

flutter test test/counter_test.dart
複製代碼

結果:

其餘運行方式能夠在這裏看:link

Widget 測試

參考文章(主要就是按這個學習翻譯的,英文 ok 能夠直接看官網):link

第一步:添加 flutter_test 包

爲何要用這個包,不用前面的 test 包呢,由於 flutter_test 包有下面這些功能:

  • 等等再補充
dev_dependencies:
  flutter_test:
    sdk: flutter
複製代碼
第二步:建立一個要測試的 Widget
class MyWidget extends StatelessWidget {
  final String title;
  final String message;

  const MyWidget({
    Key key,
    @required this.title,
    @required this.message,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: Center(
          child: Text(message),
        ),
      ),
    );
  }
}
複製代碼
第三步:寫測試代碼

有了要測試的 Widget 之後,能夠開始寫測試了:

  1. 建立一個 testWidgets 方法
  2. tester.pumpWidget 來建立一個 MyWidget
  3. finder 來在 Widget tree 中找到 title 和 message 的 Text Widgets
  4. expectfindsOneWidget 來測試這個 Widget 是否是隻出現了一次
void main() {
  // with Widgets in the test environment.
  testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
    // Create the Widget tell the tester to build it
    await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));

    // Create our Finders
    final titleFinder = find.text('T');
    final messageFinder = find.text('M');

    // Use the `findsOneWidget` matcher provided by flutter_test to verify our
    // Text Widgets appear exactly once in the Widget tree
    expect(titleFinder, findsOneWidget);
    expect(messageFinder, findsOneWidget);
  });
}
複製代碼
第四步:運行測試

發如今 Android Studio 裏郵件代碼文件點運行就能運行了:

補充

[1] 第三步中的 WidgetTester 除了 pumpWidget 還提供了其餘的方法,在使用 StatefulWidget 或者 animations 的時候能夠用到:

[2] 第三步中的 findsOneWidget 是一個 Matcher,還有一些其餘的 Matcher 能夠用:

findsOneWidget 只有一個對應的 Widget
findsNothing 沒有找到對應的 Widget
findsWidgets 找到一個或一個以上對應的 Widget
findsNWidgets 找到 N 個 Widget

最後那個查了一下 API, 是這麼用的:
expect(find.text('Save'), findsNWidgets(2));
複製代碼

集成測試

參考文章(主要就是按這個學習翻譯的,英文 ok 能夠直接看官網):link

單元測試 和 Widget 測試能夠用於測試單獨的 class, function, 和 Widget。當要測試各部分一塊兒運行或者測試一個 application 在真實設備上運行的表現的時候就要用到集成測試

第一步:建立要測試的 App

首先,要建立一個被測試的 App, 這個應該功能就是按懸浮按鈕 +1,就是初始給的那個 demo, 不同的在於這裏咱們給 TextFloatingActionButton 添加了 ValueKey 以便在測試時識別這些特色的 Widgets。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Counter App',
      home: MyHomePage(title: 'Counter App Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              // Provide a Key to this specific Text Widget. This allows us
              // to identify this specific Widget from inside our test suite and
              // read the text.
              key: Key('counter'),
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Provide a Key to this the button. This allows us to find this
        // specific button and tap it inside the test suite.
        key: Key('increment'),
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
複製代碼
第二步:添加 flutter_driver 依賴

在集成測試中要用到 flutter_driver,在 pubspec.yaml 中加入它。

dev_dependencies:
  flutter_driver:
    sdk: flutter
  test: any
複製代碼

同時,也添加了 test ,由於也要用到這裏面的方法和斷言。

第三步:寫 test 代碼
  1. 建立文件夾 test_driver(和 lib 文件同級)
  2. 在文件夾下建立兩個文件(命名能夠隨意),一個是建立指令化的 Flutter 應用程序,使咱們能 "運行" 這個app,並記錄運行的 performance (app.dart),另外一個用於寫測試來判斷app 是否是按預期運行(app_test.dart),目錄以下:
flutter_road_test/
  lib/
    main.dart
  test_driver/
    app.dart
    app_test.dart
複製代碼
第四步:建立指令化的 Flutter 應用程序

建立指令化的 Flutter 應用程序要有這兩步:

  1. 它啓用了Flutter Driver的擴展
  2. 運行 App

test_driver/app.dart 文件裏寫下這兩步:

import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_road_test/main.dart' as app;

void main() {
  // This line enables the extension
  enableFlutterDriverExtension();

  // Call the `main()` function of your app or call `runApp` with any widget you
  // are interested in testing.
  app.main();
}
複製代碼
第五步:寫測試

如今有了指令化的 app, 咱們要寫測試了,測試須要下面四步:

  1. SeralizableFinders 來定位特定的 Widgets
  2. 測試前,在 setUpAll 方法中鏈接 app
  3. 測試一些重要的狀況
  4. 當測試完,在 teardownAll 方法中斷開鏈接
// Imports the Flutter Driver API
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
  group('Counter App', () {
    // 經過 Finders 找到對應的 Widgets
    final counterTextFinder = find.byValueKey('counter');
    final buttonFinder = find.byValueKey('increment');

    FlutterDriver driver;

    // 鏈接 Flutter driver 
    setUpAll(() async {
      driver = await FlutterDriver.connect();
    });

    // 當測試完成斷開鏈接
    tearDownAll(() async {
      if (driver != null) {
        driver.close();
      }
    });

    test('starts at 0', () async {
      // 用 `driver.getText` 來判斷 counter 初始化是 0
      expect(await driver.getText(counterTextFinder), "0");
    });

    test('increments the counter', () async {
      // 首先,點擊按鈕
      await driver.tap(buttonFinder);

      // 而後,判斷是否增長了 1
      expect(await driver.getText(counterTextFinder), "1");
    });
  });
}
複製代碼
第六步:運行測試

運行一個 Android 或者 iOS 模擬器或者連上本身的手機,而後從項目根目錄下運行下面的命令:

flutter drive --target=test_driver/app.dart
複製代碼

命令的做用:

  1. 運行目標 app 並安裝
  2. 啓動 app
  3. 運行 app_test.dart 裏的測試

結果:

代碼地址

github.com/draftbk/flu…

Flutter 學習之路 Github 地址

這是項目的 GitHub 地址,正在持續更新,歡迎 Star 呀!╮( ̄▽ ̄)╭

github.com/draftbk/flu…

參考文檔

相關文章
相關標籤/搜索