LiveData 테스트를 한다면 JUnit4와 JUnit5의 Rule설정을 다르게 해줘야한다.
JUnit4에서는 Rule을 지원하지만 JUnit5에서는 더이상 지원하지 않아 Extension을 만들어 줘야한다.
testImplementation 'androidx.arch.core:core-testing:2.1.0'
class CalculatorViewModelTest {
@get:Rule
val instantExecutorRule = InstantTaskExecutorRule()
...
}
안그러면 다음과 같은 에러가 나온다.
Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
class InstantTaskExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) {
runnable.run()
}
override fun postToMainThread(runnable: Runnable) {
runnable.run()
}
override fun isMainThread(): Boolean {
return true
}
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
class CalculatorViewModelTest {
@JvmField
@RegisterExtension
val instantTaskExecutorExtension = InstantTaskExecutorExtension()
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
// 2초 동안 liveData가 바뀌지 않는다면 에러를 반환
// 무한 대기를 방지하고 다음 liveData 테스트를 빠르게 진행하기 위함
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
@Suppress("UNCHECKED_CAST")
return data as T
}
위의 코드는 LiveData는 2초 동안 값이 바뀌지 않는 경우 에러가 나오도록 구현되었습니다.
라이브 데이터는 Obserbe 패턴을 가지고 있기 때문에 비동기를 구현할 수 있습니다. 하지만 데이터가 변경되기까지 하염없이 기다릴 수는 없습니다. 그래서 위와 같은 확장함수를 구현해야 합니다.
@Test
fun `수식 1 을 입력하면 1이 보여야한다`() {
// WHEN
calculatorViewModel.appendOperand(1)
// THEN
assertThat(calculatorViewModel.statement.getOrAwaitValue()).isEqualTo("1")
}