Kotlin 单元测试 - 总是通过

问题描述 投票:0回答:1

我有一个 ViewModel,它具有一些从服务器或本地数据库获取数据的功能。

我已经使用假存储库为该 ViewModel 编写了单元测试。

但是无论是否调用存储库的函数,我的单元测试都通过了。

我尝试了一些解决方案,但没有一个有效。

我更改了我的 ViewModel 和我的假存储库。

但什么都没有改变。

这是我的视图模型:

@HiltViewModel
class AllGamesViewModel @Inject constructor(
    private val getGame: GetAllGamesUseCase,
    private val getGamesByGenre: GetGamesByGenreUseCase,
    private val getAllGamesFromLocalUseCase: GetAllGamesFromLocalUseCase,
    private val connectivityObserver: ConnectivityObserver
) : ViewModel() {
    private val _state = mutableStateOf(AllGamesState())
    val state: State<AllGamesState> = _state



    private val _eventFlow = MutableSharedFlow<UIEvent>()
    val eventFlow = _eventFlow.asSharedFlow()

    private val _status = MutableStateFlow(Status.Unavailable)
    val status: StateFlow<Status> get() = _status

    init {
        networkConnectionCheck()
    }

    private fun networkConnectionCheck() {
        viewModelScope.launch {
            connectivityObserver.observe().collect{ newStatus ->
                _status.value = newStatus
                if(_status.value == Status.Available){
                    getAllGames()
                }else{
                    getAllGamesFromLocal()
                }
            }
        }
    }

    fun getAllGames() {
        viewModelScope.launch {
            getGame().collect { result ->
                withContext(Dispatchers.Main){
                    handleResult(result)
                }
            }
        }
    }

    fun getAllGamesFromLocal(){
        viewModelScope.launch {
            getAllGamesFromLocalUseCase().collect{ result ->
                withContext(Dispatchers.Main){
                    handleResult(result)
                }
            }
        }
    }

    fun getTaggedGames(genre: String) {

        viewModelScope.launch {
            getGamesByGenre(genre).collect { result ->
                withContext(Dispatchers.Main){
                    handleResult(result)
                }
            }
        }
    }

    private fun handleResult(result:Resource<List<Game>>){
        when (result) {
            is Resource.Success ->{
                _state.value = _state.value.copy(
                    allGames = result.data ?: emptyList(),
                    isLoading = false
                )
            }
            is Resource.Error ->{
                _state.value = _state.value.copy(
                    allGames = result.data ?: emptyList(),
                    isLoading = false,
                )
                viewModelScope.launch {
                    _eventFlow.emit(
                        UIEvent.ShowSnackBar(
                            result.message!!
                        )
                    )
                }

            }
            is Resource.Loading ->{
                _state.value = _state.value.copy(
                    allGames = result.data ?: emptyList(),
                    isLoading = true,
                )
            }
        }
    }
}

我使用 Fake Repository 来测试 ViewModel,如下所示:

class FakeGameRepository:GameRepository{
    private var games = mutableListOf<Game>()
    override fun getAllGames(): Flow<Resource<List<Game>>> {
        return flow { emit(Resource.Success(games)) }
    }

    override fun getGameById(id: Int): Flow<Resource<Game>> {
        val game = games.find { it.id == id }
        return flow { emit(Resource.Success(game)) }
    }

    override fun getGameByGenre(genre: String): Flow<Resource<List<Game>>> {
        val taggedGames = mutableListOf<Game>()
        games.forEach { game ->
            if(game.genre == genre){
                taggedGames.add(game)
            }
        }
        return flow { emit(Resource.Success(taggedGames)) }
    }

    override fun getAllGamesFromLocal(): Flow<Resource<List<Game>>> {
        return flow { emit(Resource.Success(games)) }
    }

    fun insertGame(insertedGames:List<Game>){
        games.addAll(insertedGames)
    }

}

这是 ViewModel 测试:

@ExperimentalCoroutinesApi
class AllGamesViewModelTest{

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    var mainCoroutineRule = MainCoroutineRule()

    private lateinit var connectivityObserver: FakeNetworkConnectivityObserver
    private lateinit var viewModel: AllGamesViewModel
    private lateinit var getAllGamesUseCase: GetAllGamesUseCase
    private lateinit var getAllGamesFromLocalUseCase: GetAllGamesFromLocalUseCase
    private lateinit var getGamesByGenreUseCase: GetGamesByGenreUseCase
    private lateinit var repository: FakeGameRepository
    private lateinit var game: Game
    val games = mutableListOf<Game>()
    @Before
    fun setup(){

        repository = FakeGameRepository()
        connectivityObserver = FakeNetworkConnectivityObserver()
        getAllGamesUseCase = GetAllGamesUseCase(repository)
        getAllGamesFromLocalUseCase = GetAllGamesFromLocalUseCase(repository)
        getGamesByGenreUseCase = GetGamesByGenreUseCase(repository)
        viewModel = AllGamesViewModel(
            getAllGamesUseCase,
            getGamesByGenreUseCase,
            getAllGamesFromLocalUseCase,
            connectivityObserver
        )

        for(i in 0..10){
            game = Game(
                developer = "developer $i",
                freeToGameProfileUrl = "freeUrl $i",
                gameUrl = "gameUrl $i",
                genre = "genre $i",
                id = i,
                platform = "plat $i",
                publisher = "publish $i",
                releaseDate = "release $i",
                shortDescription = "short $i",
                thumbnail = "thumb $i",
                title = "title $i"
            )
            games.add(game)
        }
        repository.insertGame(games)
    }

    @Test
    fun `Test getAllGames when Network is Available`() = runTest {
        connectivityObserver.setStatus(Status.Available)
        repository.getAllGames()
        delay(500L)
        assertThat(viewModel.status.value).isEqualTo(Status.Available)
        assertThat(viewModel.state.value.allGames).containsExactlyElementsIn(games)
    }

    @Test
    fun `Test getAllGamesFromLocal when Network is Unavailable`() = runTest {
        connectivityObserver.setStatus(Status.Unavailable)
        repository.getAllGamesFromLocal()
        delay(500L)
        assertThat(viewModel.status.value).isEqualTo(Status.Unavailable)
        assertThat(viewModel.state.value.allGames).containsExactlyElementsIn(games)
    }
}
android kotlin unit-testing android-viewmodel
1个回答
0
投票

原因是FakeRepository。

因为我没有检查网络状态,所以FakeRepository中的函数将数据传递给ViewModel。

所以我像这样更改 FakeRepository:

class FakeGameRepository @Inject constructor(
    private val connectivityObserver: FakeNetworkConnectivityObserver
):GameRepository{
    private var games = mutableListOf<Game>()

    override fun getAllGames(): Flow<Resource<List<Game>>> {

        return flow {
            connectivityObserver.observe().collect{status ->
                when(status){
                    Status.Available ->{
                        emit(Resource.Success(games))
                    }
                    else ->{
                        games = emptyList<Game>().toMutableList()
                        emit(Resource.Error("No Internet Connection"))
                    }
                }
            }
        }
    }

    //...

    fun insertGame(insertedGames:List<Game>){
        games.addAll(insertedGames)
    }

}

因此,在其类型为 FakeRepository 的变量的测试类中,我像这样传递了connectivityObserver:

repository = FakeGameRepository(connectivityObserver)
© www.soinside.com 2019 - 2024. All rights reserved.