Flutterでアプリを作成する際に自動テストをしっかりと書くようにしています。
Flutterによる自動テストを3記事に分けて公開するうちの3記事目です。
本記事ではIntegrationテストについての備忘を書いていきます。
1記事目「【Flutter】Riverpod使用時の単純なユニットテストの書き方」はこちらからご覧ください。
2記事目「【Flutter】Widgetテストのシンプルな書き方」はこちらからご覧ください。
Flutterで実施できるIntegrationテストとは
一般的にIntegrationテストとは統合テストのことを指します。
統合テストでは、ひとつひとつの機能を組み合わせた、プログラム全体を統合してテストしていきます。
例えば登録画面で登録したデータが、データを表示する画面で正しく表示されているか?といったように、機能をまたいで問題なく動作しているかなどをチェックしていきます。
FlutterでのIntegrationテスト
FlutterではIntegrationテストを自動実施するための機能が公式に提供されています。
UnitテストとWidgetテストでは、Dartのコードを書いてターミナルでテストを実行していましたが、Integrationテストは、iOSシミュレーターなどのシミュレーターや実機で動作します。
実際の動作環境で動かしながらテストをすることができるので、UnitテストやWidgetテストを実施した後に最後の砦として実行することで、リリースする前の最終確認として非常に有効的です。
好きなポイントでスクリーンショットを取ることもできます。
注意)FlutterのIntegrationテストで使用する「flutter_driver」というパッケージがまだnull safety非対応です。null safety対応しているプロジェクトでは使用がかなり難しい状況です。もうすぐ対応するという情報も入っていますので、null safety対応のプロジェクトでは少々待つ必要がありそうです。
手動でテストする?
Flutterに限った話ではないですが、Integrationテストを手動で実施するケースがあります。
手動で実施する事自体は、ユーザー目線でシステムを操作してみることにもつながるので、必要なことかもしれませんが、実際に実施するとかなり時間がかかります。
企業などで実施する場合にはエビデンスを取得するために、スクリーンショットを取得したりなど、実施に時間をがっつり使ったりします。
また、人間がやることですので実際にテスト結果がNGでもOKとしてしまうことも考えられます。
自動テストを書いておけば、比較的早く、そして正確に何度もテストを実施することができるので、ソースを書かなければいけないというデメリットはありますが、一度作成してしまえば以降はほぼ※ノーリスクでテストを実行できます。
※システムに変更が入ったときに、テストコードをメンテナンスする必要が出てくることがあります。
FlutterのIntegrationテストを作成する
Integrationテストを実施するため、依存パッケージの追加とソースを2つ作成します。
pubspec.yaml
dev_dependencies:
flutter_test:
sdk: flutter
flutter_driver:
sdk: flutter
test: any
pubspec.yamlのdev_dependenciesに「flutter_driver」と「test」を追加します。
app.dart
import 'package:flutter_driver/driver_extension.dart';
import 'package:tdd_sample/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
Integrationテストを実施するために、FlutterDriverExtensionを有効にして、テストするアプリを起動する処理を書いておきます。
test.dart
test.dart内で実際にテストするコードを書いていきます。
import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
late FlutterDriver driver;
const path = "./test/screen_shot/";
setUpAll(() async {
driver = await FlutterDriver.connect();
final health = await driver.checkHealth();
if (health.status == HealthStatus.bad) {
fail("FlutterDriverの起動に失敗");
}
});
tearDownAll(() async {
driver.close();
});
test("5をタップ", () async {
await driver.tap(find.text("5"));
expect(await driver.getText(find.byValueKey("result")), "5");
await _doScreenShot(driver, path, "5をタップ");
});
}
/// スクリーンショットを取得する.
Future<void> _doScreenShot(
FlutterDriver driver, String path, String fileName) async {
await driver.waitUntilNoTransientCallbacks();
final picture = await driver.screenshot();
final file = File("$path/$fileName.png");
await file.writeAsBytes(picture);
}
setUpAllはテストの最初に1回だけ動作します。ここでFlutterDriverの接続を行います。
tearDownAllはテストの最後に1回だけ動作します。ここではFlutterDriverを閉じます。
準備ができたら、testメソッドを使用してテストをしていきます。
driver.tap(finder)を使用することで、ボタンをタップすることができます。このソースではタップしたら結果の部分が変わるので、expectメソッドで変わったことを確認しています。
また、driver.screenshotメソッドを使用することで、スクリーンショットを取得することができます。取得する画像のファイル種類はPNGになるので、保存するときに拡張子をpngにしておきましょう。
取得した画像はdartのioパッケージを使用して、指定したフォルダに保存します。
Integrationテストを実行する
IntegrationテストはFlutterの以下コマンドで実行できます。
flutter drive --target=app.dartのpath
実行すると、アプリが起動してソースに書いたテストコードが自動で動きます。
テストが完了すると、アプリが終了します。
まとめ
FlutterによるIntegrationテストの書き方についての備忘録でした。
テストコードの作成には時間がかかりますが、自動テストのコードを作成できれば手動で何度もテストをするよりも品質向上、時短ができるはずです。
効率化のためにも、テストコードをしっかりと書いていく必要があると思います。
Flutter学習情報
Flutterを勉強するのに最適な参考書は、以下の「基礎から学ぶFlutter」です。
環境構築から、Dart/Flutterの基本/テストやパフォーマンスチューニングまで一通り学ぶことができます。
また、当ブログではFlutterを初心者が学ぶためにオススメな方法を「Flutter を初心者が学ぶおすすめの勉強法!【間違いない動画があります】」という記事で公開していますので、Flutterに興味がある方は是非読んでみてください。
動画学習がしたい!という方にオススメの記事がこちらです。
Flutterの将来性はどうなのか?をまとめた記事はこちらです。