AndroidのLiveDataとかViewModelについて考えるとりとめのないこと
LiveData
はデータホルダなので、覚えておきたいことを覚えておくために使うのがよい。表示したいデータとか、画面の状態など。私は、イベントやメッセージは受けた人が覚えておくべきものではないと考えているので、イベントバスやメッセージパッシングのために使う時にはなんらかの工夫が必要だと思っている。そういうことがしたくなったらReactive extensionとかを使ったほうがいいかもしれない。ViewModel
はActivity
やFragment
ではなくViewDataBinding
に結びついていると考える。バインドしたいデータのソースが複数種類あるならViewModel
を継承したインタフェースか抽象クラスをViewDataBinding
クラスにセットできるようにしておく。Intent
やFragment
のArgumentを使ってViewModel
のクラス名を渡すなどしてやれば、Activity
やFragment
はどのViewModel
を取得すればよいかわかる。LiveData
はたとえ同じオブジェクトがset/postされてもObserver.onChanged()
を呼ぶ。これが嫌ならObservableXxx系のクラスを使ってバインドするか、BindingAdapter
で前回バインドした値を受け取って新たにセットするかどうか決めるのがいいのだろうか。MediatorLiveData
でラップしたものを使うほうがスマートかもしれない。- こういう使い方はよくないのかもしれないけど、
ViewModel
をActivity
単位で共有するように作ったかとか、Fragment
ごとに別々に持っていてもいいのかとか、ProviderのFactoryとかタグは必要なのかとか、やっているとすぐ忘れてしまうので、ViewModel
ごとにXxxViewModel.getInstance(FragmentActivity, ViewModelProviders.Factory)
みたいな感じのメソッドを用意している。 RecyclerView
を持っている画面のViewModel
をそれぞれのアイテムビュー(RecyclerView
の子要素)に渡したくなることがあるのだけど、なんとなく違和感を覚えてしまう(なんとなくなので杞憂ならそれでいいんだけど)。データだけならAdapter
の中でバインドしてしまえばいいのだと思うのだけど、クリックイベントを拾って選択状態にしたいとか画面遷移したいとかいうことをやろうとするとViewModel
でイベントをさばきたくなってしまう。RecyclerView
を持っている画面のViewModel
って他に何かやることあるのっていう気もしていて、だったら各アイテムビューにViewModel
渡してもいいじゃんと思ったり、実はそういう画面にはそもそもViewModel
いらないのでは?と思ったりもして迷宮入りしている。
TestRuleでテスト対象の初期化処理などをまとめる
テストケースのセットアップ処理を使いまわしたい時、TestRule
に実装するといろんな使いまわしが効いて便利なので最近はそうしている。個人的には、TestRule
の中には本当に基本的な準備や後始末の処理を書いて隠し、テストクラスの方に本質的なテストの前提条件だけ書くような感じで使い分けている。モックオブジェクトのセッティングやよく使うような一連のまとまった処理もTestRule
に書いたりする。UIテストのPageObjectみたいなものだろうか。Baseクラスを継承する方法でもいいと思うが、私はうまくできないので敬遠している。
実際はテストの前や後に呼ばれるメソッドが生えていて使いやすくなっているTestWatcher
クラスを継承して作る。TestWatcher
クラスの中身を見れば一目瞭然なのだが、ざっくり言うと@Before
の代わりになるのがstarting()
、@After
の代わりになるのがfinished()
で、普通はこのメソッドを実装すればよい。そのほかにテストに成功した時と失敗した時に呼ばれる、succeeded()
とfailed()
などがある。starting()
は@Before
の処理よりも必ず先に呼ばれるし、finished()
は@After
の必ず後に呼ばれる。
一つのテストクラスにはテストに必要なだけ複数のTestRule
を置くことができる。Androidのテストを書いていると、InstantTaskExecutorRule
やActivityTestRule
あたりは基本的にどの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) }
こんなかんじ。
JUnit4のEnclosedなクラスのテストが2回実行されてしまう件のイージーな回避方法
./gradlew testUnitTest --tests *Test
ちゃんとぐぐれば重複したクラスを除外するコードが出てくるので普通の人はそっちを試したほうがいいと思います。
2018/10/26 追記
Gradle4.7でこの現象が起きなくなったようです。