I want to UnitTest my AuthRepository
which is using this "flow-version" of NetworkBoundResource (find the code for that below)
My current UnitTest is green when writing it like this:
@ExperimentalCoroutinesApi
class AuthRepositoryTest {
companion object {
const val FAKE_ID_TOKEN = "FAkE_ID_TOKEN"
}
@get:Rule
val testCoroutineRule = TestCoroutineRule()
private val coroutineDispatcher = TestCoroutineDispatcher()
private val mockUserDao = mockk<UserDao>()
private val mockApiService = mockk<TimetrackerApi>()
private val sut = AuthRepository(
mockUserDao, mockApiService, coroutineDispatcher
)
@Test
fun getAuthToken_noCachedData_shouldMakeNetworkCall() = testCoroutineRule.runBlockingTest {
// Given an empty database
// When getAuthToken is called
sut.getAuthToken(FAKE_ID_TOKEN).first()
coVerify {
// Then first try to fetch data from the DB
mockUserDao.get()
}
}
}
But I would actually go deeper and verify the following logic:
coVerifyOrder {
// Then first try to fetch data from the DB
mockUserDao.get()
// Then fetch the User from the API
mockApiService.getUser(FAKE_ID_TOKEN)
// THen insert the user into the DB
mockUserDao.insert(any())
}
But the test result tells me in this case that only the first verified call was made (mockUserDao.get()
). If you have a look at the logic of networkBoundResource
(code below), you will see that the other two calls should also be made.
I also tried to do some fake calls before val data = query().first()
. In this case the first fake call was always verified, but everything after that not.
I can give more info at any time, but I think for now this should suffice to start finding out what's going on here...
The SubjectUnderTest is my very simple AuthRepository
:
class AuthRepository @Inject constructor(
private val userDao: UserDao,
private val apiService: TimetrackerApi,
private val coroutineDispatcher: CoroutineDispatcher
) {
fun getAuthToken(idToken: String): Flow<Resource<User>> {
return networkBoundResource(
query = { userDao.get() },
fetch = { apiService.getUser(idToken) },
saveFetchResult = { apiResponse ->
if (apiResponse is NetworkResponse.Success) {
val user = UserConverter.convert(apiResponse.body)
userDao.insert(user)
}
},
coroutineDispatcher = coroutineDispatcher
)
}
}
And here the code of the flow-version of networkBoundResource
.
inline fun <ResultType, RequestType> networkBoundResource(
crossinline query: () -> Flow<ResultType>,
crossinline fetch: suspend () -> RequestType,
crossinline saveFetchResult: suspend (RequestType) -> Unit,
crossinline onFetchFailed: (Throwable) -> Unit = { },
crossinline shouldFetch: (ResultType) -> Boolean = { true },
coroutineDispatcher: CoroutineDispatcher
) = flow<Resource<ResultType>> {
val data = query().first()
val flow = if (shouldFetch(data)) {
emit(Resource.loading(data))
try {
saveFetchResult(fetch())
query().map { Resource.success(it) }
} catch (throwable: Throwable) {
onFetchFailed(throwable)
query().map { Resource.error(throwable.toString(), it) }
}
} else {
query().map { Resource.success(it) }
}
emitAll(flow)
}.onStart {
emit(Resource.loading(null))
}.catch {
emit(Resource.error("An error occurred while fetching data!", null))
}.flowOn(coroutineDispatcher)
from How to UnitTest kotlin-flow version of networkBoundResource?
No comments:
Post a Comment