1

when i try to call frankfurterApi.convert with test scope, the test instantly gets terminated(println("here2") doesn't get called), but with runBlocking everything works good, just like with viewModelScope in production

test:

private lateinit var mockWebServer: MockWebServer

@Before
fun init() {
    mockWebServer = MockWebServer()
    auth = mockAuth()
    firestore = mockFirestore(listener)
}

@Test
fun requestConversion_success() = runTest {
    val currentBalance = 234f
    val from = CurrencyEnum.EUR
    val to = CurrencyEnum.USD
    val api = Retrofit.Builder().baseUrl(mockWebServer.url("/"))
        .addConverterFactory(GsonConverterFactory.create())
        .build().create(FrankfurterApi::class.java)
    val jsonResponse = """
        {
          "amount": $currentBalance,
          "base": "$from",
          "date": "2024-06-12",
          "rates": {
            "$to": 219.3
          }
        }
        """
    mockWebServer.enqueue(MockResponse().setBody(jsonResponse).setResponseCode(200))
    val datastoreManager = mockk<DataStoreManager> {
        every { balanceFlow() } returns flowOf(Pair(from.ordinal, currentBalance))
        every { themeFlow() } returns flowOf()
    }
    val repository = SettingsRepository(auth, firestore.collection("data"), api, datastoreManager)
    val viewModel = SettingsViewModel(repository, CoroutineScopeProvider(this))
    advanceUntilIdle()
    viewModel.changeCurrency(to)
    advanceUntilIdle()
    println(viewModel.uiState.value.currencyChangeResult)
    assertTrue(viewModel.uiState.value.currencyChangeResult is Result.Success)
}

view model:

@HiltViewModel
class SettingsViewModel @Inject constructor(
    private val settingsRepository: SettingsRepository,
    scopeProvider: CoroutineScopeProvider
): ViewModel() {

private val _uiState = MutableStateFlow(UiState())
private val scope = scopeProvider.provide() ?: viewModelScope
val uiState = _uiState.asStateFlow()

fun changeCurrency(newCurrency: CurrencyEnum) = scope.launch {
    val balance = _uiState.value.balance
    settingsRepository.changeCurrency(balance, newCurrency)
        .catch { updateCurrencyChangeResult(Result.Error(it.message ?: it.toString())) }
        .collect { updateCurrencyChangeResult(it) }
}

scope provider:

class CoroutineScopeProvider(private val coroutineScope: CoroutineScope? = null) {
fun provide() = coroutineScope
}

repository:

class SettingsRepository(
private val auth: FirebaseAuth,
private val firestore: CollectionReference,
private val frankfurterApi: FrankfurterApi,
private val dataStoreManager: DataStoreManager
) {
suspend fun changeCurrency(currentBalance: Balance, newCurrencyEnum: CurrencyEnum) = flow {
    val uid = auth.currentUser?.uid
    if (uid != null) {
        emit(Result.InProgress)
        println("here")
        val convertedMainBalance = frankfurterApi.convert(
                amount = currentBalance.balance,
                from = CurrencyEnum.entries[currentBalance.currency].name,
                to = newCurrencyEnum.name)
        println("here2")
        firestore.document(uid).collection("balance").document("balance")
            .set(Balance(newCurrencyEnum.ordinal, 
convertedMainBalance.rates[newCurrencyEnum.name]!!)).await()
        changeLastTimeUpdated(Instant.now().toEpochMilli())
        emit(Result.Success(""))
    }
}

api:

interface FrankfurterApi {
@GET("latest")
suspend fun convert(
    @Query("amount") amount: Float,
    @Query("from") from: String,
    @Query("to") to: String
): ExchangeCurrency

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.