「Flutter Advent Calendar 2022」に参加させて頂きます!18日目です。
現在FlutterでCI/CDを取り組んでいます。ユニットテスト、Widgetテスト、ゴールデンテスト、インテグレーションテストを実施して、Code Magicを使用してアプリストアにアップロードできればなぁ、と考えています。
その中で、実際に取り組む前に知っておいたら、楽だったのに、と思う点を記載いたします。
ユニットテスト
国際時間を使用するのであれば、時差を考慮する必要があるかもしれない。
国際時間を使っていると、手元のPCでは日本の時差を考えてテストします。しかしサーバでテストすると、サーバが日本にないことが多いです。そのため、時差を考慮する必要があるかも知れません。
final timeOffset = DateTime.now().timeZoneOffset.inHours;
test('datetime test', () async {
expect(
DateTime(2022, 12, 6, 18 + timeOffset, 37, 57).toUtc().toIso8601String(),
'2022-12-06T18:37:57.000Z',
);
Widgetテスト
特になし
ゴールデンテスト
実施環境のOSやバージョンによって、画面のキャプチャが微妙に異なる
WindowsとMacで、実施したときのキャプチャが微妙に異なる。また、MacでもOSによってはキャプチャーが異なります(「Golden テストがCIで実行すると失敗する場合の対策法」 からの「自MacOs13.1, CodeMagic 12.6.1でテストが通らないorz」)
)。
そのため、Windowsのキャプチャ画像を正しい画像としてMacでテストすると失敗します。逆も然り。
CI/CDで環境のOSとOSのバージョンを揃える必要がある。幸い手元にある8年前に買ったMacが12.5.1でCodeMagicが12.6.1でテストが通ったので、なんとかいけそうです。、
Windowsではゴールデンテストはせずに、Macだけで実施すると言う設定を記載します。
import 'dart:async';
import 'dart:io';
import 'package:golden_toolkit/golden_toolkit.dart';
/// Golden TestをMacでのみ実施する設定
Future testExecutable(FutureOr Function() testMain) async {
return GoldenToolkit.runWithConfiguration(
() async {
await loadAppFonts();
await testMain();
},
config: GoldenToolkitConfiguration(
skipGoldenAssertion: () => !Platform.isMacOS,
),
);
}
インテグレーションテスト
画面のキャプチャ
画面のキャプチャを、Androidはできる、iOSはできない(実行時に例外発生)
Androidで画面をキャプチャする場合は、binding.convertFlutterSurfaceToImage()をtestWidgetsごとに実施する必要がある
AndroidもiOSも挙動が残念なときがある
Androidでは、アニメーションがうまく動かない時がある。起動して待機中が続くときがある。(pumpではなく、pumpAndSettleだと出にくい気がします)
iOSはダイアログの中の項目をFinderが見つけてくれず、ダイアログのボタンが押せない。
プロダクトコード
テストとアプリとWEBのコードを分かつ
Platform.environment.containsKey(‘FLUTTER_TEST’)で、ユニットテスト、Widgetテスト、ゴールデンテスト実施時にコードがテストであることを知ることができる。インテグレーションテストでは使えない。
たとえば、Wiegetテストやゴールデンテストを実施時にネットワークの画像を使用すると、テストが失敗する。それを避けるために、テストであることを判断して、テスト時は別のローカル画像(FlutterLogoとか)に置き換えると良い。
ただ、FlutterWebはPlatformをサポートしていないので、FlutterWebでそのまま実施するとUnsupportedExceptionが発生する。そのため、kIsWebをその前に使って、Flutter Web時はPlatformのコードを実施させないことが重要。
また、ゴールデンテストにIsolateのcomputeを使用すると、タイムアウトになった。テストも考慮に入れるなら、Platform.environment.containsKey(‘FLUTTER_TEST’)を使って、テスト時はcomuteを使わない実装を入れる。
kIsWeb || !Platform.environment.containsKey('FLUTTER_TEST')
? Image.network(url)
: FlutterLogo(size: size),
本当ならDIで切り分けると良い。
端末の向き
そのまますると、端末の置き方によって、端末の向きが変わってしまう。DIを使って、向きを固定させる。以下はGetItを使用した例。
final orientations = GetIt.I.get<List<DeviceOrientation>>();
SystemChrome.setPreferredOrientations(
orientations,
);
「戻る」ボタン
「戻る」ボタンがOSによって異なるため、動的にIconDataを変える
final backIcon = Platform.isAndroid
? Icons.arrow_back
: Platform.isIOS
? Icons.arrow_back_ios
: throw UnsupportedError('OS用のバックアイコンを設定してください');
まだ他にもありましたら記載していきます。その他、アドバイスがあればよろしくお願いします。