添加測試庫
用於測試的庫在新建Flutter工程時已經引入,你可以打開pubspec.yaml找到相關代碼:dev_dependencies: flutter_test: sdk: flutter但是,如果工程代碼不知為何沒有引入這個庫,請按照如上所示將其添加到pubspec.yaml文件中。
創建測試類和被測試類
測試類是用來測試另一個類的,被測試類是等待另一個類來測試的。觀察整個Flutter工程的結構,來到項目的根目錄下,發現它包含test目錄。通常,該文件夾就是用來放測試類的,而將App的業務邏輯代碼放在lib目錄中。下面分別創建這兩個類:lib/counter.dart
test/counter_test.dart
如上所示,在lib目錄中新建了counter.dart類,在test目錄中新建了counter_test.dart類。前者是被測試類,後者是測試類。開發業務邏輯
編寫常規業務邏輯就等同於開發Fluter App,這裡實現一下簡單的變量自增和變量自減兩個方法。示例如下:class Counter{ int value=0; void increment() => value++; void decrement() => value--;}開發測試類
接下來,我們編寫counter_test.dart類,這裡需要導入test.dart類以便可以正常使用測試方法。在測試counter.dart類中變量自增和變量自減方法的代碼如下:import 'package:flutter_test/flutter_test.dart';import 'package:flutter_app2/counter.dart';void main(){
test('Counter value shold be',() { final counter = Counter(); counter.increment(); expect(counter.value, 1); }); test('Value should be decremented', (){ final counter = Counter(); counter.decrement(); expect(counter.value,-1); });
}除了上述寫法,如果多個測試方法屬於同一個功能或可歸為同一類,就可以使用group將其歸類,其實現如下:import 'package:flutter_test/flutter_test.dart';import 'package:flutter_app2/counter.dart';void main(){ group('Counter',() { test('value 0', () { expect(Counter().value, 0); }); test('Counter value shold be', () { final counter = Counter(); counter.increment(); expect(counter.value, 1); }); test('Value should be decremented', () { final counter = Counter(); counter.decrement(); expect(counter.value, -1); }); });}
運行測試類
最後,為了得到最終的測試結果來運行測試類。打開Android Studio中的Terminal視圖,運行集成的控制臺,在其中輸入如下命令:然後按回車鍵,稍等片刻,即可得到測試結果:
/Users/apple/tools/flutter/bin/flutter --no-color test --machine --start-paused --plain-name Counter test/counter_test.dart那麼,在不指定測試類時,Flutter框架會默認運行所有測試類。
顧名思義,所謂組件測試就是用來測試單個組件的,即用來確保某個組件按預期的方式運行。下面分步驟來介紹組件測試如何進行。添加測試庫
和單元測試一樣,運行組件測試依然需要添加測試庫,如下:dev_dependencies: flutter_test: sdk: flutter創建要被測試的組件
新建的項目包含一個MyApp組件,以及多個Material Design風格的組件,這裡測試的主要組件是MyApp。打開test目錄下的widget_test.dart文件,可以看到具體的測試步驟。創建組件測試類
打開test目錄下的widget_test.dart文件,閱讀裡面的代碼,發現用於組件測試的結構如下。import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_app2/main.dart';
void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { await tester.pumpWidget(MyApp());
expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing);
await tester.tap(find.byIcon(Icons.add)); await tester.pump();
expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); });}這裡用testWidgets()方法進行組件測試,它包含了WidgetTester對象——tester。使用WidgetTester創建組件
繼續閱讀widget_test.dart文件中的代碼,在testWidgets()方法中,開始執行以下語句:
testWidgets('Counter increments smoke test', (WidgetTester tester) async {pumpWidget()方法創建了MyApp組件,這一步並沒有在行動裝置上運行,只是在後臺默默地完成了組件的渲染。還發現tester.tap()方法模擬點擊的操作,緊跟其後的tester.pump()方法則會重建組件。因為在單純的點擊後並不會使狀態組件發生最終的顯示變化,所以只有在調用tester.pump()方法之後才行。通常,在被測試的代碼中有setState()方法就會有tester.pump()方法出現。使用find查找組件
在widget_test.dart文件中,還有類似這樣的寫法:
find.text('0')find.text('1')使用find可以找到要驗證的組件。比如,在上面的代碼中,前者是通過「包含0」去尋找符合該要求的組件。運行被測試的代碼,界面顯示所示。
點擊加號的上面變為加1,
顯而易見,符合「包含0」條件的只有屏幕中央的文本,右下角的加號按鈕則是通過加號條件找到的。使用Matcher驗證結果
在找到組件並模擬點擊事件後,就要驗證最終的結果。在widget_test.dart文件中,將查找組件和驗證結果寫到了一起。這段代碼很容易理解,通過查找「包含0」的條件,若符合「找到一個組件」的結果,則表示測試通過。widget_test.dart文件中的整段代碼如下:其邏輯如下:
(1)創建MyApp組件。
(2)查找符合「包含0」條件的組件,期望結果是1個。
(3)查找符合「包含1」條件的組件,期望結果是0個。
(4)點擊包含加號圖標的組件。
(5)重建組件。
(6)查找符合「包含0」條件的組件,期望結果是0個。
(7)查找符合「包含1」條件的組件,期望結果是1個。
運行組件測試的方法和運行單元測試的相同。
我們知道,單元測試和組件測試都是針對某個方法、某個類或某個組件而言的,它們通常不會測試這些單獨的部分組合在一起後的情況。對於這種情況,就需要用到集成測試。集成測試的進行方式和單元/組件測試的不同,它需要在設備或虛擬設備上部署指令化的程序,然後使用測試套件驅動它,最後檢查並確保程序是按照期望的方式來運行的。下面依舊分步驟來看一下集成測試是如何進行的。
創建要測試的App
由於集成測試是對整個App而言的,因此要進行集成測試就需要先創建目標App。在實際開發中,集成測試通常是在完成整個App開發後進行的。這裡,依舊使用新建的計數器應用為例,然後在計數值顯示的Text組件和FloatingActionButton組件上添加key屬性,如下:import 'package:flutter/material.dart';
void main() { runApp(MyApp());}
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter 點擊按鈕加1', theme: ThemeData( primarySwatch: Colors.green, ), home: MyHomePage(title: 'Flutter 大成 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( '您已經點擊的次數 :', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); }}添加必要的測試庫
集成測試需要使用flutter_driver庫。除了要聲明flutter_test庫,還要將flutter_driver庫添加到pubspec.yaml的dev_dependencies節點中。如下:flutter_test: sdk: flutter flutter_driver: sdk: flutter test: any創建測試類
集成測試用到的測試類位於工程根目錄下的test_driver目錄中,默認創建的Flutter工程並不包含這個目錄,需要手動新建。創建後,還需要創建指令化的Flutter應用程式類和集成測試用到的類。創建指令化的Flutter應用程式類。指令化的Flutter應用程式允許開發者運行App。本例中,將該類命名為app.dart,並將其保存在test_driver目錄下。創建集成測試用到的類。集成測試類包含用於驗證運行結果正確與否的測試套件,其同樣記錄性能分析數據。該類的命名規則為必須以與之相關的指令化應用程式類名後加_test為名。本例中,指令化的Flutter應用程式類名為app.dart,則集成測試用到的類名為app_test.dart。構建指令化的Flutter應用程式類
創建指令化的Flutter應用程式很簡單,只需要啟動Flutter drive擴展並運行程序即可。完整的app.dart代碼如下:import 'package:flutter_driver/driver_extension.dart';import 'package:flutter_app2/main.dart' as app;void main(){ enableFlutterDriverExtension(); app.main();}由於main()方法在app.dart中也有聲明,因此這裡使用了import … as …的方式導入類,並在使用時通過調用app.main()方法區分不同類但同名的main()方法。
構建集成測試用到的類
在構建好指令化的Flutter應用程式後,就可以寫針對它的測試類了。這個測試類使用Flutter driver API來指導應用程式執行操作,之後再驗證這個操作是否正確。完整的app_test.dart代碼如下:如上代碼所示,首先使用find定位要測試的組件。本例中,通過key的方式找到計數值Text組件和FloatingActionButton組件。接著,在正式測試前調用setUpAll()方法連接到App。然後,測試腳本的內容,分別對應本例中的兩個test()方法。最後,調用tearDownAll()方法和App斷開連接,結束測試流程。
運行測試
運行測試的方法是啟動集成的命令行視圖。在工程根目錄下運行以下指令:
flutter driver --target=test_driver/app.dart注意,由於集成測試會真正地在設備上運行App,因此能看到測試操作的全過程。在測試完成後,App將自動關閉,並在命令行留下相關日誌。從日誌中查看的測試結果如下:flutter driver --target=test_driver/app.dart
Running "flutter pub get" in flutter_app2... 723msLaunching test_driver/app.dart on Chrome in debug mode...Waiting for connection from debug service on Chrome... 19.8sDebug service listening on ws: