如何使用Koin进行MVVM单元测试?

3

如何使用Koin对MVVM进行单元测试?

我尝试了测试:链接

但是,我不知道为什么在ViewModelTest的fun getLookUpLeagueList()中出现了error("No Data in ViewModel")错误

Repository

class LookUpLeagueRepository {

    fun getLookUpLeague(idLeague: String): MutableLiveData<LookUpLeague> {
        val lookUpLeague = MutableLiveData<LookUpLeague>()
        APIService().getLookUpLeague(idLeague).enqueue(object : Callback<LookUpLeague> {
            override fun onFailure(call: Call<LookUpLeague>, t: Throwable) {
                d("TAG", "lookUpLeagueOnFailure ${t.localizedMessage}")
            }

            override fun onResponse(call: Call<LookUpLeague>, response: Response<LookUpLeague>) {
                lookUpLeague.value = response.body()
            }
        })
        return lookUpLeague
    }
}

视图模型

class LookUpLeagueViewModel(private val lookUpLeagueRepository: LookUpLeagueRepository) :
    ViewModel() {

    var lookUpLeagueList = MutableLiveData<LookUpLeague>()

    fun getLookUpLeagueList(idLeague: String) {
        lookUpLeagueList = lookUpLeagueRepository.getLookUpLeague(idLeague)
    }
}

模块

val lookUpLeagueModule = module {
    single { LookUpLeagueRepository() }
    viewModel { LookUpLeagueViewModel(get()) }
}

ViewModel测试

class LookUpLeagueViewModelTest : KoinTest {

    val lookUpLeagueViewModel: LookUpLeagueViewModel by inject()

    val idLeague = "4328"

    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Mock
    lateinit var observerData: Observer<LookUpLeague>

    @Before
    fun before() {
        MockitoAnnotations.initMocks(this)
        startKoin {
            modules(lookUpLeagueModule)
        }
    }

    @After
    fun after() {
        stopKoin()
    }

    @Test
    fun getLookUpLeagueList() {
        lookUpLeagueViewModel.lookUpLeagueList.observeForever(observerData)

        lookUpLeagueViewModel.getLookUpLeagueList(idLeague)

        val value = lookUpLeagueViewModel.lookUpLeagueList.value ?: error("No Data in ViewModel")

        Mockito.verify(observerData).onChanged(value)
    }
}
1个回答

2
@Test
fun getLookUpLeagueList() {
    lookUpLeagueViewModel.lookUpLeagueList.observeForever(observerData)
    ...
}

此时,lookUpLeagueList 是一个 MutableLiveData 的实例。假设这是 MutableLiveData #1

lookUpLeagueViewModel.getLookUpLeagueList(idLeague)

执行上述代码会调用LookUpLeagueViewModel.getLookUpLeagueList函数。让我们来看一下它的内部实现。
lookUpLeagueList = lookUpLeagueRepository.getLookUpLeague(idLeague)

LookUpLeagueRepository内部创建了一个全新的MutableLiveData。这不是observerData正在观察的那个MutableLiveData。此时,lookUpLeagueViewModel.lookUpLeagueList指向新的MutableLiveData #2,因为你将其重新分配给了var lookUpLeagueList

val value = lookUpLeagueViewModel.lookUpLeagueList.value ?: error("No Data in ViewModel")

因此,您实际上是在查询新的、未观察过且为空的 MutableLiveData #2。这就是为什么 valuenull 的原因。您应该将其声明为 val,而不是 var。不要重新分配变量,用 setValuepostValue 来传播更改。

抱歉先生,我遇到了新问题:
  1. 我尝试使用https://gist.github.com/afdaldev/0c3a26664ba00f90a627167546d5ecce,ViewModelTest看起来不错。但是在Fragment中我得到了一个空列表。我也尝试了https://gist.github.com/afdaldev/7bc9165a899d1398448238bb299ca87d,ViewModelTest得到observerData.onChange(null)。
- Afdal
@AfdalDev 第一个问题:Repository.getLookUpLeague 会在异步网络调用完成之前返回值。第二个问题:读取值(Test.kt 的第31行)将在异步网络调用完成之前发生。 - wooseop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接