Day19 - Flutter - 測(cè)試用例

概述

  • 單元測(cè)試
  • Widget測(cè)試
  • 集成測(cè)試
一、單元測(cè)試

單元測(cè)試是針對(duì)一個(gè)函數(shù)或者類進(jìn)行測(cè)試

  • 1.1、添加測(cè)試依賴
    test 或者 flutter_test加入依賴文件,默認(rèn)創(chuàng)建的Flutter程序已經(jīng)有了依賴,Test 包提供了編寫測(cè)試所需要的核心功能,在 pubspec.yaml 里面,dev_dependencies 下面的依賴在打包的時(shí)候是會(huì)被忽略掉的

    dev_dependencies:
       flutter_test:
         sdk: flutter
    
  • 1.2、創(chuàng)建需要測(cè)試的類
    單元測(cè)試通常是測(cè)試一個(gè)函數(shù)或者類,這個(gè)函數(shù)或者類被稱之為是一個(gè)單元。
    在這里,我們按照官方示例,創(chuàng)建一個(gè)簡(jiǎn)單的Counter類來演示:

    class Counter {
       int value = 0;
    
       void increment() => value++;
       void decrement() => value--;
    }
    
  • 1.3、創(chuàng)建測(cè)試文件
    我們?cè)趖est目錄下(注意:不是lib目錄下),創(chuàng)建一個(gè)測(cè)試文件:counter_test.dart

    • 通常測(cè)試代碼都會(huì)放在該目錄下,并且測(cè)試文件不會(huì)打包到最終的應(yīng)用程序中;

    • 測(cè)試文件通常以 _test.dart 命名,這是 test runner 尋找測(cè)試文件的慣例

      import 'package:flutter_test/flutter_test.dart';
      import 'package:test_demo/counter.dart';
      
      void main() {
          test("Counter Class test", () {
              // 1.創(chuàng)建Counter并且執(zhí)行操作
             final counter = Counter();
             counter.increment();
             // 2.通過expect來監(jiān)測(cè)結(jié)果正確與否
             expect(counter.value, 1);
          });
      }
      
  • 1.4、整合多個(gè)測(cè)試
    如果對(duì)同一個(gè)類或函數(shù)有多個(gè)測(cè)試,我們希望它們關(guān)聯(lián)在一起進(jìn)行測(cè)試,可以使用 group

    import 'dart:math';
    
    import 'package:flutter_test/flutter_test.dart';
    import 'package:test_demo/counter.dart';
    
    void main() {
       group("Counter Test", () {
           test("Counter Default Value", () {
              expect(Counter().value, 0);
           });
    
           test("Counter Increment test", () {
              final counter = Counter();
              counter.increment();
              expect(counter.value, 1);
           });
    
           test("Counter Decrement test", () {
              final counter = Counter();
              counter.decrement();
              expect(counter.value, -1);
           });
      });
    }
    
  • 1.5. 執(zhí)行測(cè)試結(jié)果
    用 IntelliJ 或 VSCode 執(zhí)行測(cè)試
    IntelliJ 和 VSCode 的 Flutter 插件支持執(zhí)行測(cè)試。用這種方式執(zhí)行測(cè)試是最好的,因?yàn)樗梢蕴峁┳羁斓姆答侀]環(huán),而且還支持?jǐn)帱c(diǎn)調(diào)試。

    • IntelliJ
      • 打開文件 counter_test.dart
      • 選擇菜單 Run
      • 點(diǎn)擊選項(xiàng) Run 'tests in counter_test.dart'
      • 或者,也可以使用系統(tǒng)快捷鍵!
    • VSCode
      • 打開文件 counter_test.dart
      • 選擇菜單 Debug
      • 點(diǎn)擊選項(xiàng) Start Debugging
      • 或者,也可以使用系統(tǒng)快捷鍵!

    在終端執(zhí)行測(cè)試
    我們也可以打開終端,在工程根目錄輸入以下命令來執(zhí)行測(cè)試:

    flutter test test/counter_test.dart
    
二、Widget測(cè)試

Widget測(cè)試主要是針對(duì)某一個(gè)封裝的Widget進(jìn)行單獨(dú)測(cè)試

  • 2.1、添加測(cè)試依賴
    Widget 測(cè)試需要先給 pubspec.yaml 文件的 dev_dependencies 段添加 flutter_test 依賴。在單元測(cè)試中我們已經(jīng)說過,默認(rèn)創(chuàng)建的Flutter項(xiàng)目已經(jīng)添加了

    dev_dependencies:
        flutter_test:
           sdk: flutter
    
  • 2.2. 創(chuàng)建測(cè)試Widget

    import 'package:flutter/material.dart';
    
    class HYKeywords extends StatelessWidget {
         final List<String> keywords;
         HYKeywords(this.keywords);
    
         @override
         Widget build(BuildContext context) {
            return Scaffold(
                body: ListView(
                 children: keywords.map((key) {
                    return ListTile(
                        leading: Icon(Icons.people),
                        title: Text(key),
                    );
                 }).toList(),
               ),
            );
          }
    }
    
  • 2.3、編寫測(cè)試代碼
    創(chuàng)建對(duì)應(yīng)的測(cè)試文件,編寫對(duì)應(yīng)的測(cè)試代碼:

    • testWidgets:flutter_test中用于測(cè)試Widget的函數(shù);
    • tester.pumpWidget:pumpWidget 方法會(huì)建立并渲染我們提供的 widget;
    • find:find() 方法來創(chuàng)建我們的 Finders;
      • findsNothing:驗(yàn)證沒有可被查找的 widgets。

      • findsWidgets:驗(yàn)證一個(gè)或多個(gè) widgets 被找到。

      • findsNWidgets:驗(yàn)證特定數(shù)量的 widgets 被找到。

        import 'package:flutter/material.dart';
        import 'package:flutter_test/flutter_test.dart';
        import 'package:test_demo/keywords.dart';
        
        void main() {
            testWidgets("KeywordWidget Test", (WidgetTester tester) async {
                 await tester.pumpWidget(MaterialApp(title: "demo", home: JKKeywords(["abc", "cba", "nba"]),) );
        
                 final abcText = find.text("abc");
                 final cbaText = find.text("cba");
                 final icons = find.byIcon(Icons.people);
        
                expect(abcText, findsOneWidget);
                expect(cbaText, findsOneWidget);
                expect(icons, findsNWidgets(2));
            });
        }
        

        官方文檔 中還有更多關(guān)于Widget的測(cè)試

三、集成測(cè)試

單元測(cè)試和Widget測(cè)試都是在測(cè)試獨(dú)立的類或函數(shù)或Widget,它們并不能測(cè)試單獨(dú)的模塊形成的整體或者獲取真實(shí)設(shè)備或模擬器上應(yīng)用運(yùn)行的狀態(tài);
這些測(cè)試任務(wù)可以交給 集成測(cè)試 來完成;
集成測(cè)試 需要有兩個(gè)大的步驟:1、發(fā)布一個(gè)可測(cè)試應(yīng)用程序到真實(shí)設(shè)備或者模擬器上;2、利用獨(dú)立的測(cè)試套件去驅(qū)動(dòng)應(yīng)用程序,檢查儀器是否完好可用;

  • 3.1、創(chuàng)建可測(cè)試應(yīng)用程序
    我們需要?jiǎng)?chuàng)建一個(gè)可以運(yùn)行在模擬器或者真實(shí)設(shè)備的應(yīng)用程序。
    這里我直接使用了官方的示例程序,但是不同的是我這里給兩個(gè)Widget添加了兩個(gè)Key

    • 顯示數(shù)字的 Text Widget:ValueKey("counter")

    • 點(diǎn)擊按鈕的 FloatingActionButton Widget:key: ValueKey("increment")

      import 'package:flutter/material.dart';
      
      void main() => runApp(MyApp());
      
      class MyApp extends StatelessWidget {
          // This widget is the root of your application.
          @override
          Widget build(BuildContext context) {
             return MaterialApp(
                title: 'Flutter Demo',
                theme: ThemeData(
                   primarySwatch: Colors.blue,
                ),
                home: MyHomePage(title: 'Flutter Demo 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',
                              key: ValueKey("counter"),
                              style: Theme.of(context).textTheme.display1,
                           ),
                        ],
                      ),
                  ),
                  floatingActionButton: FloatingActionButton(
                      key: ValueKey("increment"),
                      onPressed: _incrementCounter,
                      tooltip: 'Increment',
                      child: Icon(Icons.add),
                  ), // This trailing comma makes auto-formatting nicer for build methods.
               );
            }
      }
      
  • 3.2. 添加flutter_driver依賴
    我們需要用到 flutter_driver 包來編寫 集成測(cè)試,所以我們需要把 flutter_driver 依賴添加到應(yīng)用pubspec.yaml 文件的 dev_dependencies 位置:

    dev_dependencies:
        flutter_driver:
           sdk: flutter
        flutter_test:
           sdk: flutter
        test: any
    
  • 3.3. 創(chuàng)建測(cè)試文件
    和單元測(cè)試以及Widget測(cè)試不同的是,集成測(cè)試的程序和待測(cè)試的應(yīng)用并不在同一個(gè)進(jìn)程內(nèi),所以我們通常會(huì)創(chuàng)建兩個(gè)文件:

    • 文件一:用于啟動(dòng)帶測(cè)試的應(yīng)用程序
    • 文件二:編寫測(cè)試的代碼

    我們可以將這兩個(gè)文件放到一個(gè)文件中:test_driver,目錄結(jié)構(gòu)如下


  • 3.4. 編寫安裝應(yīng)用代碼
    安裝應(yīng)用程序代碼在 app.dart 中,分層兩步完成:

    • flutter driver 的擴(kuò)展可用
    • 運(yùn)行應(yīng)用程序

    test_driver/app.dart 文件中增加以下代碼:

    import 'package:flutter_driver/driver_extension.dart';
    import 'package:test_demo/main.dart' as app;
    
    void main() {
       // 開啟DriverExtension
       enableFlutterDriverExtension();
    
       // 手動(dòng)調(diào)用main函數(shù), 啟動(dòng)應(yīng)用程序
       app.main();
    }
    
  • 3.5. 編寫集成測(cè)試代碼
    現(xiàn)在我們有了待測(cè)應(yīng)用,我們可以為它編寫測(cè)試文件了。這包含了四個(gè)步驟:

    • 創(chuàng)建 SerializableFinders 定位指定組件
    • 在 setUpAll() 函數(shù)中運(yùn)行測(cè)試案例前,先與待測(cè)應(yīng)用建立連接
    • 測(cè)試重要場(chǎng)景
    • 完成測(cè)試后,在 teardownAll() 函數(shù)中與待測(cè)應(yīng)用斷開連接

    test_driver/app_test.dart 文件中增加以下代碼:

    import 'package:flutter_driver/flutter_driver.dart';
    import 'package:test/test.dart';
    
    void main() {
        group("Counter App Test", () {
           FlutterDriver driver;
    
           // 初始化操作
          setUpAll(() async {
             driver = await FlutterDriver.connect();
          });
    
          // 測(cè)試結(jié)束操作
          tearDownAll(() {
             if (driver != null) {
                driver.close();
             }
          });
    
          // 編寫測(cè)試代碼
          final counterTextFinder = find.byValueKey('counter');
          final buttonFinder = find.byValueKey('increment');
    
          test("starts at 0", () async {
             expect(await driver.getText(counterTextFinder), "0");
          });
    
          test("on tap click", () async {
               await driver.tap(buttonFinder);
               expect(await driver.getText(counterTextFinder), "1");
          });
       });
    }
    
  • 3.6. 運(yùn)行集成測(cè)試
    首先,啟動(dòng)安卓模擬器或者 iOS 模擬器,或者直接把 iOS 或 Android 真機(jī)連接到你的電腦上。
    接著,在項(xiàng)目的根文件夾下運(yùn)行下面的命令

    flutter drive --target=test_driver/app.dart
    
    • 這個(gè)指令的作用:
      • 創(chuàng)建 --target 目標(biāo)應(yīng)用并且把它安裝在模擬器或真機(jī)中
      • 啟動(dòng)應(yīng)用程序
      • 運(yùn)行位于 test_driver/ 文件夾下的 app_test.dart 測(cè)試套件

    運(yùn)行結(jié)果:我們會(huì)發(fā)現(xiàn)正常運(yùn)行,并且結(jié)果app中的FloatingActionButton自動(dòng)被點(diǎn)擊了一次

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容