一. 單元測試
單元測試是針對一個函數或者類進行測試
1.1. 添加測試依賴
將 test 或者 flutter_test加入依賴文件,默認創建的Flutter程序已經有了依賴:
- Test 包提供了編寫測試所需要的核心功能
<code>dev_dependencies
:flutter_test
:sdk
: flutter/<code>
1.2. 創建需要測試的類
單元測試通常是測試一個函數或者類,這個函數或者類被稱之為是一個單元。
在這裡,我們按照官方示例,創建一個簡單的Counter類來演示:
<code>class
Counter
{int
value
=0
;void
increment
() =>value
++;void
decrement
() =>value
--; }/<code>
1.3. 創建測試文件
我們在test目錄下(注意:不是lib目錄下),創建一個測試文件:counter_test.dart
- 通常測試代碼都會放在該目錄下,並且測試文件不會打包到最終的應用程序中;
- 測試文件通常以 _test.dart 命名,這是 test runner 尋找測試文件的慣例;
<code>import
'package:flutter_test/flutter_test.dart'
;import
'package:test_demo/counter.dart'
;void
main
()
{ test("Counter Class test"
, () {final
counter = Counter(); counter.increment(); expect(counter.value,1
); }); }/<code>
1.4. 整合多個測試
如果對同一個類或函數有多個測試,我們希望它們關聯在一起進行測試,可以使用group
<code>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
); }); }); }/<code>
1.5. 執行測試結果
用 IntelliJ 或 VSCode 執行測試
IntelliJ 和 VSCode 的 Flutter 插件支持執行測試。用這種方式執行測試是最好的,因為它可以提供最快的反饋閉環,而且還支持斷點調試。
- IntelliJ
- 打開文件 counter_test.dart
- 選擇菜單 Run
- 點擊選項 Run 'tests in counter_test.dart'
- 或者,也可以使用系統快捷鍵!
- VSCode
- 打開文件 counter_test.dart
- 選擇菜單 Debug
- 點擊選項 Start Debugging
- 或者,也可以使用系統快捷鍵!
在終端執行測試
我們也可以打開終端,在工程根目錄輸入以下命令來執行測試:
<code>fluttertest
test
/counter_test.dart/<code>
二. Widget測試
Widget測試主要是針對某一個封裝的Widget進行單獨測試
1.1. 添加測試依賴
Widget測試需要先給 pubspec.yaml 文件的 dev_dependencies 段添加 flutter_test 依賴。
- 在單元測試中我們已經說過,默認創建的Flutter項目已經添加了
<code>dev_dependencies
:flutter_test
:sdk
: flutter/<code>
1.2. 創建測試Widget
<code>import
'package:flutter/material.dart'
;class
HYKeywords
extends
StatelessWidget
{final
List keywords; HYKeywords(this
.keywords);Widget
build
(BuildContext context)
{return
Scaffold( body: ListView( children: keywords.map((key) {return
ListTile( leading: Icon(Icons.people), title: Text(key), ); }).toList(), ), ); } }/<code>
1.3. 編寫測試代碼
創建對應的測試文件,編寫對應的測試代碼:
- testWidgets:flutter_test中用於測試Widget的函數;
- tester.pumpWidget:pumpWidget 方法會建立並渲染我們提供的 widget;
- find:find() 方法來創建我們的 Finders;
- findsNothing:驗證沒有可被查找的 widgets。
- findsWidgets:驗證一個或多個 widgets 被找到。
- findsNWidgets:驗證特定數量的 widgets 被找到。
<code>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: HYKeywords(["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
)); }); }/<code>
官方文檔中還有更多關於Widget的測試:
- https://flutter.dev/docs/cookbook/testing/widget/tap-drag
三. 集成測試
單元測試和Widget測試都是在測試獨立的類或函數或Widget,它們並不能測試單獨的模塊形成的整體或者獲取真實設備或模擬器上應用運行的狀態;
這些測試任務可以交給 集成測試 來完成;
集成測試需要有兩個大的步驟
發佈一個可測試應用程序到真實設備或者模擬器上;
利用獨立的測試套件去驅動應用程序,檢查儀器是否完好可用;
3.1. 創建可測試應用程序
我們需要創建一個可以運行在模擬器或者真實設備的應用程序。
這裡我直接使用了官方的示例程序,但是不同的是我這裡給兩個Widget添加了兩個Key
- 顯示數字的Text Widget:ValueKey("counter")
- 點擊按鈕的FloatingActionButton Widget:key: ValueKey("increment")
<code>import
'package:flutter/material.dart'
;void
main
()
=> runApp(MyApp());class
MyApp
extends
StatelessWidget
{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;_MyHomePageState
createState
()
=> _MyHomePageState(); }class
_MyHomePageState
extends
State
<MyHomePage
> {int
_counter =0
;void
_incrementCounter
()
{ setState(() { _counter++; }); }Widget
build
(BuildContext context)
{return
Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ 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), ), ); } } /<code>
3.2. 添加flutter_driver依賴
我們需要用到 flutter_driver 包來編寫 集成測試,所以我們需要把 flutter_driver 依賴添加到應用pubspec.yaml 文件的 dev_dependencies 位置:
<code>dev_dependencies
:flutter_driver
:sdk
: flutterflutter_test
:sdk
: fluttertest
: any/<code>
3.3. 創建測試文件
和單元測試以及Widget測試不同的是,集成測試的程序和待測試的應用並不在同一個進程內,所以我們通常會創建兩個文件:
- 文件一:用於啟動帶測試的應用程序
- 文件二:編寫測試的代碼
我們可以將這兩個文件放到一個文件中:test_driver
<code> lib/ main.dart test_driver/ app.dart app_test.dart/<code>
3.4. 編寫安裝應用代碼
安裝應用程序代碼在app.dart中,分層兩步完成:
- 讓 flutter driver 的擴展可用
- 運行應用程序
test_driver/app.dart 文件中增加以下代碼:
<code>import
'package:flutter_driver/driver_extension.dart'
;import
'package:test_demo/main.dart'
as app;void
main
()
{ enableFlutterDriverExtension(); app.main(); }/<code>
3.5. 編寫集成測試代碼
現在我們有了待測應用,我們可以為它編寫測試文件了。這包含了四個步驟:
- 創建 SerializableFinders 定位指定組件
- 在 setUpAll() 函數中運行測試案例前,先與待測應用建立連接
- 測試重要場景
- 完成測試後,在 teardownAll() 函數中與待測應用斷開連接
test_driver/app_test.dart 文件中增加以下代碼:
<code>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(); }); tearDownAll(() {if
(driver !=null
) { driver.close(); } }); 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"
); }); }); }/<code>
3.6. 運行集成測試
首先,啟動安卓模擬器或者 iOS 模擬器,或者直接把 iOS 或 Android 真機連接到你的電腦上。
接著,在項目的根文件夾下運行下面的命令:
<code>flutter drive /<code>
這個指令的作用:
- 創建 --target 目標應用並且把它安裝在模擬器或真機中
- 啟動應用程序
- 運行位於 test_driver/ 文件夾下的 app_test.dart 測試套件
運行結果:我們會發現正常運行,並且結果app中的FloatingActionButton自動被點擊了一次。