【问题标题】:unit testing repository that use flow coroutine kotlin in android在 android 中使用流协程 kotlin 的单元测试存储库
【发布时间】:2020-10-19 12:34:50
【问题描述】:

我想为我的repository 编写单元测试。 repository 的方法返回 flowaddDiaryRate 方法首先 emit 加载状态然后从网络返回数据。我想验证addDiaryRate 方法发出加载然后数据响应。我写了这个测试,但我的测试失败了。错误说预期和实际是不一样的,而它们是相等的。

java.lang.AssertionError: 预期:是 但是:是 预期:是 实际:

我使用 codelab 和 google 示例的示例代码。 这是MainRepository

   class MainRepositoryImp constructor(
    private val apiService: MainApiService,
    private val suggestionModelDao: SuggestionModelDao,
    private val userDao: UserDao,
    private val babyDao: BabyDao,
    private val diaryHotDao: DiaryHotDao,
    private val diaryUserDao: DiaryUserDao,
    private val hashTagModelDao: HashTagModelDao,
    private val diaryTopDao: DiaryTopDao,
    private val diaryResultSearchDao: DiaryResultSearchDao,
    private val rateModelDao: RateModelDao,
    private val medalDao: MedalDao,
    private val blogModelDao: BlogModelDao,
    private val seenDiaryDao: SeenDiaryDao,
    private val rateModelAppDao: RateModelAppDao,
    private val diarySavedDao: DiarySavedDao,
    private val diaryAnotherUserDao: DiaryAnotherUserDao,
    private val anotherUserInfoDao: AnotherUserInfoDao,
    private val followDao: FollowDao,
    private val suggestProductDao: SuggestProductDao,
    private val blogSuggestWithHashTagDao: BlogSuggestWithHashTagDao,
    private val suggestHashtagDao: SuggestHashtagDao,
    private val sessionManager: SessionManager
) : MainRepository {
 override fun addRateToDiary(rate: String, diaryId: String): Flow<DataState<RateResponse>> =
        flow {
            emit(DataState.loading(true))

            val apiResult = safeApiCall(
                sessionManager.isConnectedToTheInternet()
            ) {
                apiService.addRate(
                    diaryId,
                    AddRateRequest(rate),
                    sessionManager.cachedAccessToken.value ?: ""
                )
            }

            emit(
                object : ApiResponseHandler<RateResponse, RateResponse>(
                    response = apiResult
                ) {
                    override suspend fun handleSuccess(resultObj: RateResponse): DataState<RateResponse> {
                        safeCacheCall {
                            diaryHotDao.updateRateStateDiary(
                                resultObj.data.symbolPath ?: "",
                                diaryId,
                                resultObj.data.rate
                            )
                            var countRate = 0
                            var userId = 0
                            userDao.fetchUser()?.let {
                                countRate = it.countRates + 1
                                userId = it.id
                            }
                            userDao.updateCountRate(countRate, userId)
                        }
                        return DataState.data(resultObj)
                    }

                }.getResult()
            )
        }
}

这是我的测试课:

@ExperimentalCoroutinesApi
@RunWith(RobolectricTestRunner::class)
@InternalCoroutinesApi
class MainRepositoryTest
{
    private lateinit var repository: MainRepository
    private var apiService = FakeUnitTestApiService()

    private lateinit var suggestionModelDao: SuggestionModelDao
    private lateinit var userDao: UserDao
    private lateinit var babyDao: BabyDao
    private lateinit var diaryHotDao: DiaryHotDao
    private lateinit var diaryUserDao: DiaryUserDao
    private lateinit var hashTagModelDao: HashTagModelDao
    private lateinit var diaryTopDao: DiaryTopDao
    private lateinit var diaryResultSearchDao: DiaryResultSearchDao
    private lateinit var rateModelDao: RateModelDao
    private lateinit var medalDao: MedalDao
    private lateinit var blogModelDao: BlogModelDao
    private lateinit var seenDiaryDao: SeenDiaryDao
    private lateinit var rateModelAppDao: RateModelAppDao
    private lateinit var diarySavedDao: DiarySavedDao
    private lateinit var diaryAnotherUserDao: DiaryAnotherUserDao
    private lateinit var anotherUserInfoDao: AnotherUserInfoDao
    private lateinit var followDao: FollowDao
    private lateinit var suggestProductDao: SuggestProductDao
    private lateinit var blogSuggestWithHashTagDao: BlogSuggestWithHashTagDao
    private lateinit var suggestHashtagDao: SuggestHashtagDao


    private lateinit var sessionManager: SessionManager
    private lateinit var db: AppDatabase

    private lateinit var prefManager: PrefManager

    @Rule
    @JvmField
    val instantExecutorRule = InstantTaskExecutorRule()


    @ExperimentalCoroutinesApi
    @get:Rule
    var mainCoroutineRule = MainCoroutineRule()

    @Before
    fun init() {
      val app = ApplicationProvider.getApplicationContext<Application>()

        prefManager=PrefManager(app)
        db = Room.inMemoryDatabaseBuilder(
            app,
            AppDatabase::class.java
        ).allowMainThreadQueries().build()
        initDao()
        sessionManager = SessionManager(
            app,
            userDao,
            babyDao,
            diaryHotDao,
            diaryTopDao,
            diaryUserDao,
            hashTagModelDao,
            medalDao,
            suggestionModelDao,
            diaryResultSearchDao,
            rateModelDao,
            blogModelDao,
            seenDiaryDao,
            rateModelAppDao,
            diarySavedDao,
            diaryAnotherUserDao,
            anotherUserInfoDao,
            followDao,
            suggestProductDao,
            blogSuggestWithHashTagDao,
            suggestHashtagDao,
            prefManager
        )
  
        repository = MainRepositoryImp(
            apiService,
            suggestionModelDao,
            userDao,
            babyDao,
            diaryHotDao,
            diaryUserDao,
            hashTagModelDao,
            diaryTopDao,
            diaryResultSearchDao,
            rateModelDao,
            medalDao,
            blogModelDao,
            seenDiaryDao,
            rateModelAppDao,
            diarySavedDao,
            diaryAnotherUserDao,
            anotherUserInfoDao,
            followDao,
            suggestProductDao,
            blogSuggestWithHashTagDao,
            suggestHashtagDao,
            sessionManager
        )
    }

    private fun initDao() {
        suggestionModelDao = db.getSuggestionModelDao()
        userDao = db.getUserDao()
        babyDao = db.getBabyDao()
        diaryHotDao = db.getDiaryHotDao()
        diaryUserDao = db.getDiaryUserDao()
        hashTagModelDao = db.getHashTagModelDao()
        diaryTopDao = db.getDiaryTopDao()
        diaryResultSearchDao = db.getDiaryResultSearchDao()
        rateModelDao = db.getRateModelDao()
        medalDao = db.getMedalDao()
        blogModelDao = db.getBlogModelDao()
        seenDiaryDao = db.getSeenDiaryDao()
        rateModelAppDao = db.getRateModelAppDao()
        diarySavedDao = db.getDiarySavedDao()
        diaryAnotherUserDao = db.getDiaryAnotherUserDao()
        anotherUserInfoDao = db.getAnotherUserInfoDao()
        followDao = db.getFollowDao()
        suggestProductDao = db.getBlogProductDao()
        blogSuggestWithHashTagDao = db.getBlogSuggestWithHashTagDao()
        suggestHashtagDao = db.getBlogSuggestDao()
    }


    @Test
    fun addRateTest() = mainCoroutineRule.runBlockingTest{
        /** GIVEN  **/
        val response = RateResponse(
            RateModel(
                id = "1",
                symbolPath = "symbol path",
                rate = 3,
                point = 2f,
                diaryId = "333",
                count = "34"
            )
        )

        /** WHEN **/
        repository.addRateToDiary("2","333").collect{

            assertThat(it, `is`(DataState.loading<RateResponse>(true)))

            assertThat(it,`is`(DataState.data<RateResponse>(response)))
        }
    }

【问题讨论】:

    标签: android unit-testing kotlin-coroutines flow


    【解决方案1】:

    终于找到了解决办法。 1- 我使用runBlocking 而不是mainCoroutineRule.runBlockingTest。 2-为了验证flow的所有发射值,我们可以使用toList()方法。 这是我的 fakeApiService:

        open class FakeUnitTestApiService(
        var addRateImpl: suspend (
            id: String,
            addRateRequest: AddRateRequest,
            token: String
        ) -> GenericApiResponse<RateResponse> = notImplemented2()
    ) : MainApiService {
        companion object {
            private fun <T, R> notImplemented1(): suspend (t: T) -> R {
                return { t: T ->
                    TODO("")
                }
            }
    
            private fun <T, R> notImplemented2(): suspend (t: T, s: T, l: T) -> R {
                return { t: T, s: T, l: T ->
                    TODO("")
                }
            }
        }
    
        override suspend fun addRate(
            id: String,
            addRateRequest: AddRateRequest,
            token: String
        ): GenericApiResponse<RateResponse> = addRateImpl(id,addRateRequest,token)
    }
    

    这是我的测试功能:

    @Test
    fun addRateTest() = runBlocking{
        /** GIVEN  **/
        val response = RateResponse(
            RateModel(
                id = "1",
                symbolPath = "symbol path",
                rate = 3,
                point = 2f,
                diaryId = "333",
                count = "34"
            )
        )
    
        /** WHEN **/
        apiService.addRateImpl = { s: String, addRateRequest: AddRateRequest, s1: String ->
            GenericApiResponse.create(Response.success(response))
        }
        val list=repository.addRateToDiary("2","333").toList()
    
        /**THEN**/
        assertThat(list.first().loading.isLoading,`is`(true))
        assertThat(list.last().data?.peekContent(),`is`(response))
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-13
      • 2020-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多