みなさん、Flutter life楽しんでいますか?僕は、ゼミが終わって絶賛楽しんでます!!笑
この記事では、ドメイン駆動設計(以下、DDD)でFlutterのテストをどのように設計、実装したのかについてお話しします!
お品書き
- この記事を書こうと思ったきっかけ
- 設計&実装方法 〜DDDを添えて〜
- テストを設計設計する上で何を意識したか?
- まとめ
- 参考記事
この記事を書こうと思ったきっかけ
Flutterは爆速でプロダクト(スマホアプリ)を開発するには非常に最適なフレームワークです。また、初心者でもとっつきやすい仕様になっており、誰でも簡単にある程度なら実装できるようになっています。しかし、それが仇となって、現在様々なプロジェクトにおいて弊害が生じているとよく耳にします。
特に、自分はプロダクトの保守性が担保されないことという大きな課題があると考えています。
具体的に挙げると、チームの中で初心者と上級者が混じることでコードの書き方がバラバラになってしまうことや、それにより開発速度の低下やコード同士の依存関係の増幅、不明なバグが生じるなど様々あります。そして、このような状況では綺麗にテストが書けません。。。
そこで、本記事では依存を極力なくす形にするためにDDDを採用したテストの方針を決めて実装する方法を紹介します。
設計&実装方法
一例となるプロジェクトのディレクトリ構造は以下の通りとなっています。
lib
└─repositories # DBと直接接続する唯一の層
└─view_models # UIとrepository, service層の架け橋
└─services # repositoryの責務以外のロジックがまとまった層
└─views # UI層
...
それぞれ実行したいファイルごとに mock を作成します。
mock は自動生成されるので生成したいファイルだけ指定すればいい感じに生成してくれます。
また、一つの層に対して一つのファイルでその層の mock を管理します。
mock ファイルのディレクトリ構造は以下となっています。
#
lib
└─mock
└─repository
└─repository.dart # 生成したいクラスを指定するファイル
└─repository.mocks.dart # 自動生成後のファイル
└─service
具体的な記述方法は以下の通りです。
// mock生成ファイル(repository.dart)
import 'package:mockito/annotations.dart';
import 'package:artisan_app/repositories/auth/auth_repository.dart';
import 'package:artisan_app/repositories/daily_report/daily_report_repository.dart';
// 複数mockを指定することができます
// @GenerateNiceMocks([MockSpec<生成したいクラス名>()])
@GenerateNiceMocks([MockSpec<AuthRepository>()])
@GenerateNiceMocks([MockSpec<DailyReportRepository>()])
// ここのimportの記述は必須です
import 'repository.mocks.dart';
その後、自動生成するために以下のコマンドを実行します。
flutter pub run build_runner build --delete-conflicting-outputs
テストに関するディレクトリ構造は、mock層と同様に各層ごとにフォルダを作成します。
lib
└─test
└─repository
└─service
テストコードの記述方法は、以下の通りです。
※あくまで一例なため、その時に必要なテスト方法で実行してください。
// testファイル
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
// testしたいmockじゃないクラスを指定する
@GenerateMocks([AuthRepository])
void main() {
// testの内容はよしなに書いていきましょう!!!
test('unit Test', () {
var test = 5;
expect(test, 5);
});
}
実行方法は以下の通りです。
flutter test test/repository/auth_repository.test.dart
テストを設計する上で何を意識したか?
主に意識したことは、
- 各層に対応したテストをかいた
- 優先順位を決めた
- mockをばらけないように一か所に集約した
ことです。具体的に説明します。
-
今回はDDDを採用していますが各プロジェクトによって採用するアーキテクチャは異なるのでどんな層の分け方をしても対応できるような形に設計しました。
-
今回の事例ではすでに開発が進んでおりファイル量が非常に多い状態でした。そのため、絶対にテストが必要なview_model、そしてrepository, serviceと分けました。さらに、view_modelの中でも、次のリリースまでにテストの準備した方がいいものを分別して行いました。今回の設計だと問題なくその流れでもテストを実装することができました。
-
mockを一つの箇所に集約させたことにより、バラバラになったモックが整理されていない状況にならずに済みました。
最後に
ここまでFlutterのテストをDDDで行う場合についてお話ししていきましたが、いかがでしたか?
この記事ではテストの具体的な書き方については触れませんでしたが、おそらくproviderやriverpodを使用すると少しややこしい書き方になるので、これに関しては別途記事を書きたいと思います。
この記事が誰かのお役に立てましたら非常に幸いです!
以下が参考記事です。
参考記事
Flutter RiverpodでDIしたクラスをMockitoでモック化してUnitTestを書く | ZUMA Lab
Mock dependencies using Mockito
Mockitoを使ってDartでのTDDを加速させよう - Uzabase for Engineers
An introduction to unit testing
Mock dependencies using Mockito
https://github.com/dart-lang/mockito