fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

TestRuleでテスト対象の初期化処理などをまとめる

テストケースのセットアップ処理を使いまわしたい時、TestRuleに実装するといろんな使いまわしが効いて便利なので最近はそうしている。個人的には、TestRuleの中には本当に基本的な準備や後始末の処理を書いて隠し、テストクラスの方に本質的なテストの前提条件だけ書くような感じで使い分けている。モックオブジェクトのセッティングやよく使うような一連のまとまった処理もTestRuleに書いたりする。UIテストのPageObjectみたいなものだろうか。Baseクラスを継承する方法でもいいと思うが、私はうまくできないので敬遠している。

実際はテストの前や後に呼ばれるメソッドが生えていて使いやすくなっているTestWatcherクラスを継承して作る。TestWatcherクラスの中身を見れば一目瞭然なのだが、ざっくり言うと@Beforeの代わりになるのがstarting()@Afterの代わりになるのがfinished()で、普通はこのメソッドを実装すればよい。そのほかにテストに成功した時と失敗した時に呼ばれる、succeeded()failed()などがある。starting()@Beforeの処理よりも必ず先に呼ばれるし、finished()@Afterの必ず後に呼ばれる。

一つのテストクラスにはテストに必要なだけ複数のTestRuleを置くことができる。Androidのテストを書いていると、InstantTaskExecutorRuleActivityTestRuleあたりは基本的にどのEnclosedなテストケースでも使うことになり、毎回これを書くのは面倒である。こういったTestRuleも、テスト対象のセットアップをするTestRuleの中に入れ込んでしまうとよい。具体的にはRuleChainを使って必要なTestRuleをまとめるなどして、テストケースの為に作ったTestRule.apply()の中でRuleChainのapplyを呼んでやる。

override fun apply(base: Statement?, description: Description?): Statement {
    return RuleChain.outerRule(...)
            .around(...)
            .around(...)
            .apply(super.apply(base, description), description)
}

こんなかんじ。

なんの脈絡もないがAndroidの人なのであやかっておく。 peaks.cc

JUnit4のEnclosedなクラスのテストが2回実行されてしまう件のイージーな回避方法

./gradlew testUnitTest --tests *Test

ちゃんとぐぐれば重複したクラスを除外するコードが出てくるので普通の人はそっちを試したほうがいいと思います。

2018/10/26 追記

Gradle4.7でこの現象が起きなくなったようです。

今どんな感じでアプリ開発を進めているかまとめてみる

仕事でAndroidアプリ開発をやって行く中で、自分のスタイルができつつあるので整理してみる。今作っているアプリによるところが大きいと思うので別のケースに対応できるかどうかはわからない。いろいろありそうなのでいくつかに分けてまとめることにする。続くかどうかはわからない。

作っているアプリのこと

  • SNSアプリ
  • phoneオンリー
  • 画像をダウンロードして表示する処理は無い
  • アナリティクス用のイベント取得や送信をする処理は無い
  • minimum sdk level: 19
  • ジャバ

最初の目標:Activityの状態と画面遷移のルールをActivityのViewModelに落とし込む

  • トップレベルメニューの画面をActivityにし、それ以外の画面はいずれかのActivityの中にさしこむFragmentとする
    • ActivityにToolbarとFragment用のコンテナになるViewGroupを持たせ、Activity内の画面遷移はタイトルの変更とFragmentの差し替えで実現する
  • Activityの状態をViewModelで管理する
    • Activity内で表示するFragmentやダイアログをActivityの状態と考える。便宜的に遷移中の状態を定義してもよいことにする
    • ViewModelの中でLiveDataに状態をsetして、Activityでobserveして状態に対応する画面を作る(FragmentManager.beginTransaction()やstartActivity()を呼ぶ)
  • 状態と遷移のルールを洗い出してViewModelのテストを書く
    • PlantUMLを使って状態遷移図を描いている
    • Repository(またはUseCase)はモックする
    • 状態ごとに内部クラスに分ける
    • TestRuleで初期化処理などを共通化しておくと便利

JetpackのNavigationでやってくれるようなことを自分で実装しているだけなのかもしれない。仕事のコードを乗せることはできないが、似たようなことを別の自分の趣味アプリでやって公開できれば説明しやすくなるだろうか。