[英]Unit test ViewModel using repository pattern and LiveData
I want to write a unit test for my viewmodel class:我想为我的视图模型 class 编写一个单元测试:
class MainViewModel(
repository: ShowRepository
) : ViewModel() {
private val _shows = repository.shows
val shows: LiveData<MyResult<List<Show>>>
get() = _shows
}
Here is my repository class:这是我的存储库 class:
class ShowRepository(
private val dao: ShowDao,
private val api: TVMazeService,
private val context: Context
) {
/**
* A list of shows that can be shown on the screen.
*/
val shows = resultLiveData(
databaseQuery = {
Transformations.map(dao.getShows()) {
it.asDomainModel()
}
},
networkCall = { refreshShows() })
/**
* Refresh the shows stored in the offline cache.
*/
suspend fun refreshShows(): MyResult<List<Show>> =
try {
if (isNetworkAvailable(context)) {
val shows = api.fetchShowList().await()
dao.insertAll(*shows.asDatabaseModel())
MyResult.success(shows)
} else {
MyResult.error(context.getString(R.string.failed_internet_msg))
}
} catch (err: HttpException) {
MyResult.error(context.getString(R.string.failed_loading_msg))
} catch (err: UnknownHostException) {
MyResult.error(context.getString(R.string.failed_unknown_host_msg))
} catch (err: SocketTimeoutException) {
MyResult.error(context.getString(R.string.failed_socket_timeout_msg))
}
}
And here is my Dao class:这是我的刀 class:
@Dao
interface ShowDao {
/**
* Select all shows from the shows table.
*
* @return all shows.
*/
@Query("SELECT * FROM databaseshow")
fun getShows(): LiveData<List<DatabaseShow>>
}
Here is my unit test:这是我的单元测试:
@ExperimentalCoroutinesApi
class MainViewModelTest {
private lateinit var viewModel: MainViewModel
private lateinit var repository: ShowRepository
private val api: TVMazeService = mock()
private val dao: ShowDao = mock()
private val context: Context = mock()
@Test
fun fetch() {
val observer1: Observer<List<DatabaseShow>> = mock()
dao.getShows().observeForever(observer1)
repository = ShowRepository(dao, api, context)
val observer2: Observer<MyResult<List<Show>>> = mock()
repository.shows.observeForever(observer2)
viewModel = MainViewModel(repository)
val observer3: Observer<MyResult<List<Show>>> = mock()
viewModel.shows.observeForever(observer3)
verify(viewModel).shows
}
}
But I receive following exception:但我收到以下异常:
java.lang.NullPointerException
at com.android.sample.tvmaze.viewmodel.MainViewModelTest.fetch(MainViewModelTest.kt:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
I appreciate if you give me any guideline.如果您给我任何指导,我将不胜感激。
I change my Dao method to return Flow instead of LiveData:我将我的 Dao 方法更改为返回 Flow 而不是 LiveData:
@Dao
interface ShowDao {
/**
* Select all shows from the shows table.
*
* @return all shows.
*/
@Query("SELECT * FROM databaseshow")
fun getShows(): Flow<List<DatabaseShow>>
}
And I could successfully run my tests such as:我可以成功运行我的测试,例如:
@Test
fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
mockkStatic("com.android.sample.tvmaze.util.ContextExtKt")
every {
context.isNetworkAvailable()
} returns true
`when`(api.fetchShowList()).thenReturn(Calls.response(Response.success(emptyList())))
`when`(dao.getShows()).thenReturn(flowOf(emptyList()))
val repository = ShowRepository(dao, api, context, TestContextProvider())
val viewModel = MainViewModel(repository).apply {
shows.observeForever(resource)
}
try {
verify(resource).onChanged(Resource.loading())
verify(resource).onChanged(Resource.success(emptyList()))
} finally {
viewModel.shows.removeObserver(resource)
}
}
I would mock repository and instruct mockito true Mockito.when().doReturn()
to return some data, and verify that the LiveData
output is correct.我将模拟存储库并指示 mockito true
Mockito.when().doReturn()
返回一些数据,并验证LiveData
output 是否正确。
Of course you could use an instance ShowRepository
.当然,您可以使用实例
ShowRepository
。 You will still need to instruct mockito on how to return when the execution hits the mocked object.您仍然需要指示 mockito 在执行到模拟的 object 时如何返回。 As before you can change the behaviour w
和以前一样,您可以更改行为 w
This line is wrong verify(viewModel).shows
.这一行是错误的
verify(viewModel).shows
。 Verify can be called only on mocks.验证只能在模拟上调用。
viewModel
is an instance, hence, the moment the execution hits that line, your test will fail. viewModel
是一个实例,因此,一旦执行到该行,您的测试就会失败。
For unit testing LiveData, you might need the following rule对于 LiveData 单元测试,您可能需要以下规则
@get:Rule
var rule: TestRule = InstantTaskExecutorRule()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.