為さねば成らぬ

retia.verno@gmail.com

PowerMock辛いよねという話

今触っているアプリは僕がJoinした時点でPowerMockを使っていたわけだが、非常に辛いです。

class HogehogeManager() {
  private val fugafuga: Fugafuga
}
val mockedManager = PowerMockito.spy(Whitebox.newInstance(HogehogeManager::class.java))
Whitebox.setInternalState(mockedManager, "fugafuga", Fugafuga())

field名をStringで渡してmockさせる。型も何もない。辛い・・・

通常のmockライブラリだとダメだけどPowerMockが必要になるケース、次の2つだと思ってます。

  1. privateメソッドのモック
  2. staticなメソッドのモック

ですが、これらをモックしたいという時点で何かがおかしいはず。

private メソッド

privateメソッドは辿っていけばどこかのpublicなメソッドから呼び出されているはずで、そのpublicメソッドを十分テストできれば透過的にprivateメソッドも正しく動いているはずです。 それでもprivateメソッドをテストしたくなる場合には設計が悪い可能性があります。 privateメソッドにするのではなく、そのメソッドの実行を責務に持つクラスに切り出して使います。

例えば何か計算してTextViewに表示するActivity.

class HogeActivity: Activity {
  override fun onCreate(savedInstanceState: Bundle){
    ...
    val value = calculate(1,2)
    textView.setText(value.toString())
  }

  private fun calculate(i: Int, j: Int): Int {
    ...
  }
}

この場合privateメソッドであるcalculateはそもそもHogeActivityにあるのが間違いであり、別のCalculatorのようなクラスに切り出すべき。

class HogeActivity: Activity {
  override fun onCreate(savedInstanceState: Bundle) {
    ...
    val value = Calculator().calculate(1,2)
    textView.setText(value.toString())
  }
}

class Calculator{
  fun calculate(i: Int, j: Int): Int {
    ...
  }
}

staticメソッド

ステートレスなものであれば、そもそもstaticメソッドをモックする必要がないはず。なぜならメソッドの引数と返り値の組みが常に一定だからで、モックではなく実体を用いれば良いからです。 ステートフルな、例えばSingletonオブジェクトを持つような場合は、これも設計が悪い可能性があります。 staticなメソッドはコードの至る所から触られる可能性があり、どの状態でも叩かれても大丈夫にしないといけないため。 Singletonなオブジェクトを各所で引き回したいのであれば、DIを使って渡していくべき。

結論

設計を改善してPowerMockをはがそう!