實驗 Flutter 的三種測試方法--- 代碼Github地址html
前段時間去紐約的 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
在 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 和 expect 都來自 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 裏面包含了三個測試(初始狀態測試,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
參考文章(主要就是按這個學習翻譯的,英文 ok 能夠直接看官網):link
爲何要用這個包,不用前面的 test 包呢,由於 flutter_test 包有下面這些功能:
dev_dependencies:
flutter_test:
sdk: flutter
複製代碼
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 之後,能夠開始寫測試了:
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, 這個應該功能就是按懸浮按鈕 +1,就是初始給的那個 demo, 不同的在於這裏咱們給 Text 和 FloatingActionButton 添加了 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,在 pubspec.yaml 中加入它。
dev_dependencies:
flutter_driver:
sdk: flutter
test: any
複製代碼
同時,也添加了 test ,由於也要用到這裏面的方法和斷言。
flutter_road_test/
lib/
main.dart
test_driver/
app.dart
app_test.dart
複製代碼
建立指令化的 Flutter 應用程序要有這兩步:
在 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, 咱們要寫測試了,測試須要下面四步:
// 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
複製代碼
命令的做用:
結果:
這是項目的 GitHub 地址,正在持續更新,歡迎 Star 呀!╮( ̄▽ ̄)╭