Flutter公式のドキュメント
https://docs.flutter.dev/testing/integration-tests
はじめに、テスト対象とするOSのバージョンとデバイスを決めてください。
デバイスやOSバージョンの普及率などから、テスト範囲を決められるといいと思います。
https://developer.apple.com/jp/support/app-store/
packages/skimie/integration_test/test_setting.dart
上記のファイルのコメントアウトを外すことで、テスト対象とすることができます。
XcodeのインストールされているPCで行ってください。
テストの流れ
利用可能なエミュレータの情報を取得し、Mapに変換する
テストに必要なデバイスがインストールされているかをチェックし、なければ追加する
エミュレーターを起動し、テストする
エミュレーターを終了し、キャッシュを削除する
3. に戻り、テスト対象のOSバージョンとデバイスが終わるまで繰り返す
final result = await Process.run('xcrun', ['simctl', 'list']);
上記のコードで、使用可能なエミュレーターの一覧の文字列が取得できるの、正規表現を使って、
Mapに変換する
test_setting.dart でテスト対象にしているOSバージョンのデバイスが 1. で作成したMapに存在するかをチェックする。
ない場合は、インストールする必要があるので、
final result = await Process.run(
'xcrun',
[
'simctl',
'create',
targetDevice,
targetDevice,
'com.apple.CoreSimulator.SimRuntime.iOS-$major-$miner'
],
);
上記のコードで、追加する。
// エミュレーター起動
await Process.run('xcrun', ['simctl', 'boot', device.id]);
上記のコードで、エミュレーターを起動する。
// テスト開始
final result = await Process.run(
'flutter',
[
'test',
'integration_test/app_test.dart',
'--dart-define=FLAVOR=stg',
'--dart-define=INTEGRATION_TEST=true',
'-d',
device.id,
],
);
上記のコードで、テストを開始する。
packages/skimie/integration_test/app_test.dart に書かれているテストが実行される。
// シミュレーター終了
print('Test Device Shut Down');
await Process.run('xcrun', ['simctl', 'shutdown', device.id]);
// キャッシュの削除
await Process.run('xcrun', ['--kill-cache']);
上記のコードで、エミュレーターを終了し、念のため、キャッシュを削除しておく。
3〜5の操作をFor in で回しているので、指定されたテスト範囲が終了するまで、テストを回す。
Android Studioをインストールしてある、PCで行ってください。
iOSの場合とテストの流れは、大きくは変わりません。
テストの流れ
利用可能なエミュレータの情報を取得し、Mapに変換する
テストに使用するPCのCPUアーキテクチャを確認します
テストに必要なデバイスがインストールされているかをチェックし、なければ追加する
必要なシステムイメージがインストールされているかチェックする、なければ追加する
エミュレーターを起動し、テストする
エミュレーターを終了する。
5. に戻り、テスト対象のOSバージョンとデバイスが終わるまで繰り返す
final result = await Process.run('emulator', ['-list-avds']);
上記のコードで、使用可能なエミュレーターの一覧の文字列が取得できるの、正規表現を使って、
Mapに変換する
// CPUアーキテクチャを判別して、インストールするsystem imageを選択
Future<String> getSystemStringImagesType() async {
if (Platform.isMacOS) {
final result =
await Process.run('sysctl', ['-n', 'machdep.cpu.brand_string']);
if ((result.stdout as String).contains('Apple')) {
return 'arm64-v8a';
}
return 'x86_64';
}
return 'x86_64';
}
Widndowの場合は、'x86_64'になり、Macは、Apple Silicon(M1など)の場合があるので確認が必要。
エミュレーターをインストールするときに指定する必要がある。
テストの対象になっているOSバージョンのエミュレーターがない場合は、インストールする。
await Process.run(
'avdmanager',
[
'create',
'avd',
'-n',
id,
'-k',
'system-images;android-$targetApi;google_apis_playstore;$systemImageType',
'-d',
targetDevice.deviceSetup,
],
);
上記のコードで、エミュレーターがインストールされる。
テストの対象になっているシステムイメージのチェックをする
final result = await Process.run('sdkmanager', ['--list']);
上記のコードで、インストールされているシステムイメージのリストが取得できる。
正規表現を使用して、文字列を加工し、テストに必要なシステムイメージがあれば、インストールする
// テストに必要なsystem-imageをインストール
for (final systemImage in filteredLines) {
await Process.run('sdkmanager', [systemImage]);
}
// エミュレーター起動
final process = await Process.start(
'emulator',
['-avd', device.id, '-no-snapshot'],
);
上記のコードで、エミュレーターを起動。
// テスト開始
final result = await Process.run(
'flutter',
[
'test',
'integration_test/app_test.dart',
'--dart-define=FLAVOR=stg',
'--dart-define=INTEGRATION_TEST=true',
'--device-id',
deviceId
],
);
上記のコードで、テストを開始する。
packages/skimie/integration_test/app_test.dart に書かれているテストが実行される。
await Process.run(
'adb',
['-s', deviceId, 'emu', 'kill'],
);
上記のコードで、エミュレーターを終了。
5〜6の操作をFor in で回しているので、指定されたテスト範囲が終了するまで、テストを回す。
繰り返しているテストは、packages/skimie/integration_test/app_test.dart に書かれているので、
テストはこのファイルに追加する。
intro_test や email_test の様にそれぞれのテストをディレクトリで分けて、追加していくと、
可読性が良いかと思います。
import 'package:authenticator/authenticator.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:integration_test/integration_test.dart';
import 'package:skimie/main.dart' as app;
import 'login_page/email_login_test.dart' as email_login;
import 'intro_page/intro_test.dart' as intro_test;
// Test実行
// dart integration_test/test.dart
// 実機やエミュレーターでテストする場合は、localeに気をつけてください。
// エミュレーターの場合、'en'になっていることが多いかと思います。
void main() async {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Integration Test', (
WidgetTester tester,
) async {
// アプリ起動
await app.main();
await tester.pumpAndSettle();
// ログアウトしておく
final container = ProviderContainer();
final authenticator = container.read(authenticatorProvider);
await authenticator.signOut();
await tester.pumpAndSettle();
// イントロ画面の動作確認
await intro_test.test(tester);
// Emailログインのテスト
await email_login.test(tester);
});
}