Androidのプロジェクトでユニットテストを書いているときに、テスト対象のメソッドの中でLog.d
などを呼び出していると、以下のようなエラーが出る。
Method e in android.util.Log not mocked. See https://developer.android.com/training/testing/local-tests#mocking-dependencies for details.
java.lang.RuntimeException: Method e in android.util.Log not mocked. See https://developer.android.com/training/testing/local-tests#mocking-dependencies for details.
at android.util.Log.e(Log.java)
これは、Log
クラスがAndroidフレームワークに依存しているため、それをJVM上で動かすときに、モックを作成する必要があるということを意味している。
なので、MockK
を使ってモックすることにする。おそらく次のようになるだろう。
@Before
fun setup() {
mockkStatic(Log::class)
every { Log.v(any(), any()) } returns 0
every { Log.d(any(), any()) } returns 0
every { Log.i(any(), any()) } returns 0
every { Log.e(any(), any()) } returns 0
}
ただ、毎回これをテストごとに書くのはとても面倒。なのでルールとして作成してみた。
import android.util.Log
import io.mockk.every
import io.mockk.mockkStatic
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class MockLogRule: TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
mockkStatic(Log::class)
every { Log.v(any(), any()) } returns 0
every { Log.d(any(), any()) } returns 0
every { Log.i(any(), any()) } returns 0
every { Log.e(any(), any()) } returns 0
try {
base.evaluate()
} catch (e: Throwable) {
throw e
}
}
}
}
}
ルールを作成する際は、必ずTestRule
を継承する必要があることを忘れてはいけない。
そして、apply
メソッドをオーバーライドして、Statement
を返す必要がある。
実際にテストで使う場合は、次のようになる。
class RepositoryTest {
...
@get:Rule
val mockLogRule = MockLogRule()
@Test
fun xxx_xxx_xxx() = testScope.runTest {
...
}
}
これで、毎回@Before
でモックを作成する必要がなくなったのでテストを書く際の効率化に繋がったと思う。
参考