マルチモジュールなプロジェクトでテストはどう変わるか?Nozomi Takuma2019/2/7 DroidKaigi2019
View Slide
自己紹介Nozomi TakumaSWET@DeNA所属 (2018/3~)Androidとテストが好き
はじめに
マルチモジュールって?:app:featureA :featureBアプリの実装を複数のモジュールに分割する
:appfeatueA packagefeatureB packageこれまでのアーキテクチャappモジュール内に実装をまとめたモノリシックな構成
:app:featureA :featureB:appfeatueA packagefeatureB package
app/build.gradle
なぜマルチモジュールにするのか?● 差分ビルドによるビルド時間短縮のため● App BundleやInstant App対応のため● それぞれの機能を分離させることで機能間の独立性を保つため
マルチモジュールプロジェクトのテストで変わらないこと● テストを書くこと● テストの書きかた
マルチモジュールプロジェクトのテストで変わる3つのポイント1. DI2. テスト方針3. メトリクス収集
DI
DI(Dependency Injection)って?● コンポーネントの依存を外から渡せるようにする● テストをしやすくするためのパターンとして利用される● DI LibraryとしてDaggerが有名
マルチモジュールでのDI悩みポイント1. モジュール間の依存関係をクリーンに保ちたい2. 依存の定義をモジュール内で完結させたい
モジュール間の依存をクリーンに保ちたいappmodulefeaturemodule依存解決にapp module内のクラスが必要な場合、循環参照が発生する可能性がある
Daggerで困る例
Daggerで困る例feature module内のActivityapp module内のApplication
依存の定義をモジュール内で完結させたい● どのように依存が解決されるかの定義はモジュール内で完結できるとうれしい● DaggerでいうとModuleクラスの定義など
マルチモジュールでのDIDIのやりかたやDIライブラリの使い方によっては、前述の悩みポイントに対応するため書き方や構成の変更が必要になる
話しきれないので...『マルチモジュールプロジェクトでのDagger2を用いたDependency Injection』02/07 11:20 - 11:50@Hall A
サンプル● Dagger-Androidhttps://github.com/tkmnzm/MultiModulePlayground
テスト方針
モジュール化によって得られるメリット● ビルド時間短縮による開発速度向上● 意味のあるまとまりに分けることによって他モジュールへの影響範囲が小さくなる
動作確認もモジュール内で完結できるようにしたらどうだろう?モジュール化のメリットを得るために
モジュール内で動作確認を完結できると● 各モジュールの動作確認をするために全体のapkをビルドしなくてよい● 動作確認済みのモジュールを使用することで、モジュールを結合したときの動作確認が楽になる
モジュール内でどんな動作確認ができていたら嬉しいか?テスト方針を考える問いかけ
とあるモノリシックなプロジェクトの例PresenterUseCaseRepository呼び出していることを意味する矢印
とあるモノリシックなプロジェクトの例PresenterUseCaseRepositoryユニットテスト
● レイヤー内の代表的なクラスのユニットテストは充実していた(UseCase,Repoなど)● しかし上記以外の実装もたくさんありテストが手薄なところもあったとあるモノリシックなプロジェクトの例
● このモジュールの動作確認はいまあるテストだけだと足りない気がする...● この例の場合、UI部分やITテストが不足していたモジュールとして分割してみた時
● まとまりの責務を考えるきっかけ● モノリシックなプロジェクトではレイヤーごとにどうテストするかを考えることが多かったが、モジュール毎という新しい視点が生まれるモジュールとして切り出す
● モジュール内のテスト● モジュールをまたいだテストモジュールのテスト方針を考えてみよう
モジュール内のテスト
モジュールの設計時に考えたいこと● モジュールの責務● モジュールのテスト範囲
自動テストの手段はいろいろITテストユニットテストUIテスト
例えばこんなモジュール①UtilityUtilityUtility
例えばこんなモジュール①UtilityUtilityUtilityユニットテスト
例えばこんなモジュール②LogicLogicAndroidPF
LogicLogicAndroidPF例えばこんなモジュール②ユニットテストITテスト
LogicLogicAndroidPF例えばこんなモジュール②ITテスト
例えばこんなモジュール③ActivityLogicAndroidPF
ActivityLogicAndroidPF例えばこんなモジュール③UIテストITテスト
ActivityLogicAndroidPFUIテスト例えばこんなモジュール③
方針考えたけど何から手を付ける?ユニットテストで十分なところはユニットテストからはじめるユニットテスト
導入の容易さITテストユニットテストUIテスト
FBサイクルの速さITテストユニットテストUIテスト
ITテスト・UIテストユニットテストでは動作確認として不足している部分に追加していくしかし、自分自身はAndroid PFが絡むテストは避けがちだった
ITテスト・UIテストユニットテストでは動作確認として不足している部分に追加していくしかし、自分自身はAndroid PFが絡むテストは避けがちだったAndroidのAPIが絡むテストも導入しやすくなるよう環境が変わりつつある
Project Nitrogen実行環境の差分を統一した● API● Test Runner
APIRobolectric上でも実機上でも実行可能
Test Runner
Project Nitrogenに期待すること● ローカルでのテスト:Robolectric上で素早く実行● CIでのテスト:実機やVirtual Device上などfidelity(忠実度)の高い環境で実行
現状は...● Robolectricで実行するには:src/testにテストコードを配置● Deviceで実行するには:src/androidTestにテストコードを配置
ワークアラウンド
モジュール内のテストまとめ● モジュールの責務とテスト範囲を考える● まずはユニットテストからはじめる● ITテストやUIテストはつまづきがちだが、導入しやすい環境が整いつつある
もしテストを書く時間がなくても● テスタビリティは意識して実装したい● いざテストを書こうとしたときにテストが書きにくい状態になっているとまずはリファクタリングが必要になる
モジュールをまたいだテスト
テストの方針Module BModule AModule AModule Bモジュールごとにテスト 結合してテスト
モジュールごとにテストFeatureBをモック化
結合してテスト実インスタンスやSpyを使用
モジュールをまたぐといっても● テストの書き方はいままでと変わらない● 他モジュールへの依存を外から渡すようにしていれば、これまでどおりテストダブルが使用できる
どれを選択する?● ケース・バイ・ケース● 自分のプロダクトではどんな不具合が起きやすいか?(単体でも拾える?結合しないとだめ?)というのは判断に使えそう
テストメトリクス収集
テストメトリクスAndroid開発でよく使われているのは● JUnit実行結果● Jacoco
マルチモジュールでのテストメトリクス収集各モジュールのレポートをまとめて見たい場合、結果のマージが必要
Jacocoについては...『Spek2+MockK+JaCoCoでイケてるUnitTest環境を手に入れろ!』02/08 11:20-11:50@Room 1
PITJava、その他JVM言語用MutationTestingツールhttp://pitest.org/
Mutation Testing● プロダクトコードを機械的に変更し、変更されたコードに対してテストを実行● テストが失敗するかを確認することで、テストコードが振る舞いの変更を検知できるかチェックする
例: Conditionals Boundary Mutatorif (hoge <= 5) {// do something}if (hoge < 5) {// do something}hogeが5のときの振る舞いがMutatorによって変更
例: Conditionals Boundary Mutatorif (hoge <= 5) {// do something}if (hoge < 5) {// do something}hogeが5のときの振る舞いがMutatorによって変更hogeが5のときのテストが正しく書かれていれば失敗するはず...
例: Conditionals Boundary Mutatorテストを実行し...● テストが失敗する: OK● テストが成功する: NG境界値チェックなど、不足しているテストケースを見つける手助けをしてくれる
PITのレポート
PITのうれしいところ● Mutation CoverageとLine Coverageが見られる● Android用GradlePluginがある● Kotlinのサポートが進んでいる
実行結果のマージ: マージ処理抜粋
実行結果のマージ: PIT設定抜粋
サンプルhttps://github.com/tkmnzm/MultiModulePlayground● PIT導入● PITレポートのマージ
まとめ
1. DI2. テスト方針3. メトリクス収集まとめ● DIのやりかたによっては書き方や構成の変更が必要● 詳細はその他のDI関連セッションで!
1. DI2. テスト方針3. メトリクス収集まとめ● モジュール設計時にモジュールのテスト方針を考えてみる● IT・UIテストもハードルが下がりつつある
1. DI2. テスト方針3. メトリクス収集まとめ● 各モジュールの結果をマージする必要がある● 不足しているテストケースを見つけてくれるPITをご紹介
ご清聴ありがとうございました