一. 單元測(cè)試
單元測(cè)試是針對(duì)一個(gè)函數(shù)或者類進(jìn)行測(cè)試
1.1. 添加測(cè)試依賴
將 test
或者 flutter_test
加入依賴文件勃痴,默認(rèn)創(chuàng)建的Flutter程序已經(jīng)有了依賴:
- Test 包提供了編寫測(cè)試所需要的核心功能
dev_dependencies:
flutter_test:
sdk: flutter
1.2. 創(chuàng)建需要測(cè)試的類
單元測(cè)試通常是測(cè)試一個(gè)函數(shù)或者類陈惰,這個(gè)函數(shù)或者類被稱之為是一個(gè)單元诅愚。
在這里揍拆,我們按照官方示例厂财,創(chuàng)建一個(gè)簡(jiǎn)單的Counter類來(lái)演示:
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.通過(guò)expect來(lái)監(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è)試
我們也可以打開終端,在工程根目錄輸入以下命令來(lái)執(zhí)行測(cè)試:
flutter test test/counter_test.dart
二. Widget測(cè)試
Widget測(cè)試主要是針對(duì)某一個(gè)封裝的Widget進(jìn)行單獨(dú)測(cè)試
1.1. 添加測(cè)試依賴
Widget測(cè)試需要先給 pubspec.yaml
文件的 dev_dependencies
段添加 flutter_test
依賴啊奄。
- 在單元測(cè)試中我們已經(jīng)說(shuō)過(guò)渐苏,默認(rèn)創(chuàng)建的Flutter項(xiàng)目已經(jīng)添加了
dev_dependencies:
flutter_test:
sdk: flutter
1.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(),
),
);
}
}
1.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()
方法來(lái)創(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: 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));
});
}
官方文檔中還有更多關(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è)試
來(lái)完成人断;集成測(cè)試需要有兩個(gè)大的步驟
- 發(fā)布一個(gè)可測(cè)試應(yīng)用程序到真實(shí)設(shè)備或者模擬器上;
- 利用獨(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
包來(lái)編寫 集成測(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
lib/
main.dart
test_driver/
app.dart
app_test.dart
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)擊了一次。
參考:小碼哥Flutter