diff --git a/README.md b/README.md index 31360007a..390dea7b9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ An Android Application built on top of the MifosX Self-Service platform for end- ## Join Us on Slack -Mifos boasts an active and vibrant contributor community, Please join us on [slack](https://mifos.slack.com/). Once you've joined the mifos slack community, please join the `#mifos-mobile` channel to engage with mifos-mobile development. If you encounter any difficulties joining our Slack channel, please don't hesitate to open an issue. This will allow us to assist you promptly or send you an invitation. +Mifos boasts an active and vibrant contributor community, Please join us on [slack](https://join.slack.com/t/mifos/shared_invite/zt-2f4nr6tk3-ZJlHMi1lc0R19FFEHxdvng). Once you've joined the mifos slack community, please join the `#mifos-mobile` channel to engage with mifos-mobile development. If you encounter any difficulties joining our Slack channel, please don't hesitate to open an issue. This will allow us to assist you promptly or send you an invitation. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 242a1cf0c..fece058ee 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -143,6 +143,8 @@ dependencies { testImplementation(libs.junit) testImplementation("org.mockito:mockito-core:5.4.0") implementation("org.mockito:mockito-core:5.4.0") + //turbine + testImplementation("app.cash.turbine:turbine:1.1.0") implementation("org.mockito:mockito-android:5.4.0") androidTestImplementation((libs.junit)) androidTestImplementation("org.mockito:mockito-core:5.4.0") diff --git a/app/src/debug/java/org/mifos/mobile/api/ObserveUiState.kt b/app/src/debug/java/org/mifos/mobile/api/ObserveUiState.kt new file mode 100644 index 000000000..9e624a5a1 --- /dev/null +++ b/app/src/debug/java/org/mifos/mobile/api/ObserveUiState.kt @@ -0,0 +1,16 @@ +package org.mifos.mobile.api + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.mifos.mobile.utils.BeneficiaryUiState + +//fun TestScope.obserrveUiState(): MutableList { +// val uiStates = mutableListOf() +// viewModel.beneficiaryUiState.onEach { +// println(it) +// uiStates.add(it) +// } +// .launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler))) +// return uiStates +//} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/home/HomeViewModel.kt b/app/src/main/java/org/mifos/mobile/ui/home/HomeViewModel.kt index b4ecbeee7..67830c35a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/home/HomeViewModel.kt +++ b/app/src/main/java/org/mifos/mobile/ui/home/HomeViewModel.kt @@ -109,8 +109,8 @@ class HomeViewModel @Inject constructor(private val homeRepositoryImp: HomeRepos */ private fun getLoanAccountDetails(loanAccountList: List): Double { var totalAmount = 0.0 - for ((_, _, _, _, _, _, _, _, _, _, _, _, _, _, loanBalance) in loanAccountList) { - totalAmount += loanBalance + for (loanAccount in loanAccountList) { + totalAmount += loanAccount.loanBalance } return totalAmount } @@ -124,8 +124,8 @@ class HomeViewModel @Inject constructor(private val homeRepositoryImp: HomeRepos */ private fun getSavingAccountDetails(savingAccountList: List?): Double { var totalAmount = 0.0 - for ((_, _, _, _, _, accountBalance) in savingAccountList!!) { - totalAmount += accountBalance + for (savingAccount in savingAccountList!!) { + totalAmount += savingAccount.accountBalance } return totalAmount } diff --git a/app/src/main/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModel.kt b/app/src/main/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModel.kt index 167b3a947..f20d22aa6 100644 --- a/app/src/main/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModel.kt +++ b/app/src/main/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModel.kt @@ -1,5 +1,6 @@ package org.mifos.mobile.viewModels +import android.util.Log import android.view.View import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -26,9 +27,9 @@ class BeneficiaryApplicationViewModel @Inject constructor(private val beneficiar fun loadBeneficiaryTemplate() { viewModelScope.launch { - _beneficiaryUiState.value = BeneficiaryUiState.Loading + _beneficiaryUiState.value = BeneficiaryUiState.Loading beneficiaryRepositoryImp.beneficiaryTemplate().catch { - _beneficiaryUiState.value = + _beneficiaryUiState.value = BeneficiaryUiState.ShowError(R.string.error_fetching_beneficiary_template) }.onCompletion { _beneficiaryUiState.value = BeneficiaryUiState.SetVisibility(View.VISIBLE) diff --git a/app/src/main/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModel.kt b/app/src/main/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModel.kt index a0e1d4573..a57419c27 100644 --- a/app/src/main/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModel.kt +++ b/app/src/main/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModel.kt @@ -25,7 +25,7 @@ class ThirdPartyTransferViewModel @Inject constructor( ) : ViewModel() { - private lateinit var accountOptionAndBeneficiary: AccountOptionAndBeneficiary + lateinit var accountOptionAndBeneficiary: AccountOptionAndBeneficiary private val _thirdPartyTransferUiState = MutableStateFlow(ThirdPartyTransferUiState.Initial) val thirdPartyTransferUiState: StateFlow get() = _thirdPartyTransferUiState @@ -51,6 +51,7 @@ class ThirdPartyTransferViewModel @Inject constructor( ) } catch (e: Throwable) { + println(e) _thirdPartyTransferUiState.value = ThirdPartyTransferUiState.Error(R.string.error_fetching_third_party_transfer_template) } diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 47abea6ed..5e036a592 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -29,10 +29,10 @@ ছোট টেক্সট গ্রাহক অ্যাকাউন্ট স্বাগতম পর্দা - রক্ষা + সঞ্চয় ঋণ ক্রেডিট অ্যাকাউন্ট - ভাগ + শেয়ার আপনার অ্যাকাউন্ট শেয়ার করুন একটি গ্রাহক চয়ন করুন কাজ @@ -57,7 +57,7 @@ ঋণ পরিশোধ করার কারণ ঋণ অ্যাকাউন্ট সফলভাবে প্রত্যাহার করা হয়েছে একটি ঋণ জমা দিন - জন্য নতুন ক্রেডিট আবেদন + নতুন ঋণ ক্রেডিট আবেদন আপনার ক্রেডিট আবেদন আপডেট করুন আগ্রহের ধরন ঘাত-শোষণ @@ -95,7 +95,7 @@ স্থানান্তর ধরনের নির্বাচন করুন সঞ্চয় স্থানান্তর সঞ্চয় থেকে স্থানান্তর - ঋণ জন্য ফি + ঋণের জন্য ফি জমা সক্রিয় বন্ধ @@ -118,7 +118,7 @@ ই-মেল ঠিকানা যাচাই মোড খালি - আমদানি QR + ইমপোর্ট QR গ্যারান্টি দেখুন একটি গ্যারান্টি যোগ করুন গ্যারান্টি সরান @@ -139,7 +139,7 @@ আপনার সাথে কোন লেনদেন আছে আপনার সাথে যুক্ত কোন পরিশোধের সময়সূচী নেই কোন লেনদেন উপলব্ধ নেই - কোন লেনদেন + কোন লেনদেন DOB পাওয়া যায় নি। কোন গ্রুপ থেকে unassigned @@ -197,7 +197,7 @@ অ্যাকাউন্টের অবস্থা পণ্যের নাম মোট পেমেন্ট - মি। প্রয়োজনীয় বল। + ন্যূনতম প্রয়োজনীয় ব্যালেন্স পণ্যের নাম ঋণ উদ্দেশ্য পরিচালক @@ -244,9 +244,9 @@ ঋণ আবেদন সফলভাবে প্রত্যাহার কিছু না সব - 4 সপ্তাহ - তিন মাস - 6 মাস + ৪ সপ্তাহ + ৩ মাস + ৬ মাস ফিল্টার শুরু তারিখ সমাপ্তির তারিখ @@ -265,7 +265,7 @@ আপনি একটি ইন্টারনেট সংযোগ আছে তা নিশ্চিত করুন উত্তরাধিকারী উত্তরাধিকার পরিচালনা করুন - ইসরাঈলের + সুবিধাভোগী উত্তরাধিকার বিবরণ উত্তরাধিকারীর নাম অ্যাকাউন্ট টাইপ @@ -292,7 +292,7 @@ অ্যাকাউন্ট ওভারভিউ মোট ঋণের পরিমাণ দেখান বা লুকান ***** - সার্ভের + সার্ভে অ্যাক্টিভেশন তারিখ গ্রুপ ব্যবহারকারীর তথ্য @@ -303,11 +303,11 @@ আপনি আপনার অ্যাকাউন্টে কর্ম সঞ্চালন করতে পারবেন না, সঞ্চয় অ্যাকাউন্ট থেকে QR কোড স্ক্যান করুন অথবা অন্য ব্যবহারকারীর ঋণ অ্যাকাউন্ট স্ক্যান করুন যোগ স্ক্যান - 4-সংখ্যার অ্যাক্সেস কোডটি প্রবেশ করান - অ্যাক্সেস কোড 4 ডিজিট গঠিত হওয়া উচিত + ৪ সংখ্যার অ্যাক্সেস কোডটি প্রবেশ করান + অ্যাক্সেস কোড ৪ ডিজিট হওয়া উচিত ভুল অ্যাক্সেস কোড আপনি 3 বার বেশি ভুল অ্যাক্সেস কোড প্রবেশ করেছেন - লাফালাফি করা + এড়িয়ে যান সংরক্ষণ করুন লগ ইন পিন সেট করুন পিন পুনরায় লিখুন @@ -320,7 +320,7 @@ লগ আউট করুন এখানে স্ব পরিষেবা অ্যাপ্লিকেশনটি ডাউনলোড করুন: https://play.google.com/store/apps/details?id= আবেদন নির্বাচন করুন - দ্য + পাওয়া যায় না QR জমা দিন QR কোড সঙ্গে অঞ্চল নির্বাচন করুন diff --git a/app/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt index 28b802d93..e9b4293f5 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/AccountsRepositoryImpTest.kt @@ -1,15 +1,18 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mifos.mobile.api.DataManager import org.mifos.mobile.models.client.ClientAccounts +import org.mifos.mobile.util.checkForUnsuccessfulOperation import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.mock @@ -36,31 +39,27 @@ class AccountsRepositoryImpTest { } @Test - fun loadAccounts_Success() = runBlocking { + fun loadAccounts_Success() = runTest { val mockAccountType = "savings" val mockClientAccounts = mock(ClientAccounts::class.java) `when`(dataManager.getAccounts(mockAccountType)).thenReturn((mockClientAccounts)) val resultFlow = accountsRepositoryImp.loadAccounts(mockAccountType) - val resultAccounts = resultFlow.first() - - assert(resultAccounts == mockClientAccounts) + resultFlow.test { + assertEquals(mockClientAccounts, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun loadAccounts_Error() = runBlocking { + @Test(expected = Exception::class) + fun loadAccounts_Error() = runTest { val mockAccountType = "savings" - val mockError = IOException("Network error") - `when`(dataManager.getAccounts(mockAccountType)).thenThrow(mockError) - - val resultFlow = accountsRepositoryImp.loadAccounts(mockAccountType) - var isErrorThrown = false - try { - resultFlow.first() - } catch (e: Exception) { - isErrorThrown = true - assert(e is IOException) + `when`(dataManager.getAccounts(mockAccountType)) + .thenThrow(Exception("Error occurred")) + val result = accountsRepositoryImp.loadAccounts(mockAccountType) + result.test { + assert(Throwable("Error occurred") == awaitError()) } - assert(isErrorThrown) } + } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt index 1c8b2f00c..4005ab8d9 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/BeneficiaryRepositoryImpTest.kt @@ -1,9 +1,14 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Rule @@ -14,6 +19,7 @@ import org.mifos.mobile.models.beneficiary.Beneficiary import org.mifos.mobile.models.beneficiary.BeneficiaryPayload import org.mifos.mobile.models.beneficiary.BeneficiaryUpdatePayload import org.mifos.mobile.models.templates.beneficiary.BeneficiaryTemplate +import org.mifos.mobile.util.checkForUnsuccessfulOperation import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.MockitoAnnotations @@ -39,127 +45,139 @@ class BeneficiaryRepositoryImpTest { } @Test - fun testBeneficiaryTemplate_Successful() = runBlocking { - val success: Response = - Response.success(mock(BeneficiaryTemplate::class.java)) + fun testBeneficiaryTemplate_Successful() = runTest { + val success = mock(BeneficiaryTemplate::class.java) - `when`(dataManager.beneficiaryTemplate()).thenReturn(success) - - val result = beneficiaryRepositoryImp.beneficiaryTemplate() + `when`(dataManager.beneficiaryTemplate()) + .thenReturn(success) + val resultFlow = beneficiaryRepositoryImp.beneficiaryTemplate() + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).beneficiaryTemplate() - assertEquals(result, success) - } - - @Test - fun testBeneficiaryTemplate_Unsuccessful() = runBlocking { - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - `when`(dataManager.beneficiaryTemplate()).thenReturn(error) - - val result = beneficiaryRepositoryImp.beneficiaryTemplate() - + } + + @Test(expected = Exception::class) + fun testBeneficiaryTemplate_Unsuccessful() = runTest { + `when`(dataManager.beneficiaryTemplate()) + .thenThrow( Exception("Error occurred")) + val resultFlow= beneficiaryRepositoryImp.beneficiaryTemplate() + resultFlow.test { + assert(Throwable("Error occurred") == awaitError()) + } verify(dataManager).beneficiaryTemplate() - assertEquals(result, error) - } + } @Test - fun testCreateBeneficiary_Successful() = runBlocking { - val success: Response = - Response.success(mock(ResponseBody::class.java)) + fun testCreateBeneficiary_Successful() = runTest { + val success = mock(ResponseBody::class.java) val beneficiaryPayload = mock(BeneficiaryPayload::class.java) - `when`(dataManager.createBeneficiary(beneficiaryPayload)).thenReturn(success) - - val result = beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload) + `when`(dataManager.createBeneficiary(beneficiaryPayload)) + .thenReturn(success) + val resultFlow = beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload) + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).createBeneficiary(beneficiaryPayload) - assertEquals(result, success) } - @Test - fun testCreateBeneficiary_Unsuccessful() = runBlocking { - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) + @Test(expected = Exception::class) + fun testCreateBeneficiary_Unsuccessful() = runTest { val beneficiaryPayload = mock(BeneficiaryPayload::class.java) - `when`(dataManager.createBeneficiary(beneficiaryPayload)).thenReturn(error) - - val result = beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload) - - verify(dataManager).createBeneficiary(beneficiaryPayload) - assertEquals(result, error) - } - - @Test - fun testUpdateBeneficiary_Successful() = runBlocking { - val success: Response = - Response.success(mock(ResponseBody::class.java)) - - val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java) - - `when`(dataManager.updateBeneficiary(123L, beneficiaryUpdatePayload)).thenReturn(success) - - val result = beneficiaryRepositoryImp.updateBeneficiary(123L, beneficiaryUpdatePayload) - assertEquals(result, success) - } - - @Test - fun testUpdateBeneficiary_Unsuccessful() = runBlocking { - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - - val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java) - - `when`(dataManager.updateBeneficiary(123L, beneficiaryUpdatePayload)).thenReturn(error) - - val result = beneficiaryRepositoryImp.updateBeneficiary(123L, beneficiaryUpdatePayload) - assertEquals(result, error) - } - - - @Test - fun testDeleteBeneficiary_Successful() = runBlocking { - val success: Response = - Response.success(mock(ResponseBody::class.java)) - - `when`(dataManager.deleteBeneficiary(123L)).thenReturn(success) - - val result = beneficiaryRepositoryImp.deleteBeneficiary(123L) - assertEquals(result, success) - } - - @Test - fun testDeleteBeneficiary_Unsuccessful() = runBlocking { - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.deleteBeneficiary(123L)).thenReturn(error) - - val result = beneficiaryRepositoryImp.deleteBeneficiary(123L) - assertEquals(result, error) - } - - @Test - fun testBeneficiaryList_Successful() = runBlocking { - val success: Response?> = - Response.success(Beneficiary::class.java) as Response?> - - `when`(dataManager.beneficiaryList()).thenReturn(success) - - val result = beneficiaryRepositoryImp.beneficiaryList() - assertEquals(result, success) - } - - @Test - fun testBeneficiaryList_Unsuccessful() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.beneficiaryList()).thenReturn(error) - - val result = beneficiaryRepositoryImp.beneficiaryList() - assertEquals(result, error) + `when`(dataManager.createBeneficiary(beneficiaryPayload)).thenThrow(Exception("Error occurred")) + + val resultFlow = beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload) + resultFlow.test { + assert(Throwable("Error occurred") == awaitError()) + verify(dataManager).createBeneficiary(beneficiaryPayload) + } + + @Test + fun testUpdateBeneficiary_Successful() = runTest { + val success = mock(ResponseBody::class.java) + + val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java) + + `when`(dataManager.updateBeneficiary(123L, beneficiaryUpdatePayload)) + .thenReturn(success) + + val resultFlow = + beneficiaryRepositoryImp.updateBeneficiary(123L, beneficiaryUpdatePayload) + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } + verify(dataManager).updateBeneficiary(123L, beneficiaryUpdatePayload) + + } + + @Test + fun testUpdateBeneficiary_Unsuccessful() = runTest { + + val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java) + + `when`(dataManager.updateBeneficiary(123L, beneficiaryUpdatePayload)).thenThrow( + Exception("Error occurred") + ) + + val result = beneficiaryRepositoryImp.updateBeneficiary(123L, beneficiaryUpdatePayload) + result.test { + assert(Throwable("Error occurred") == awaitError()) + } + } + + + @Test + fun testDeleteBeneficiary_Successful() = runTest { + val success = mock(ResponseBody::class.java) + + `when`(dataManager.deleteBeneficiary(123L)).thenReturn(success) + + val resultFlow = beneficiaryRepositoryImp.deleteBeneficiary(123L) + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } + verify(dataManager).deleteBeneficiary(123L) + } + + @Test + fun testDeleteBeneficiary_Unsuccessful() = runTest { + `when`(dataManager.deleteBeneficiary(123L)) + .thenThrow(Exception("Error occurred")) + val result = beneficiaryRepositoryImp.deleteBeneficiary(123L) + result.test { + assert(Throwable("Error occurred") == awaitError()) + } + } + + @Test + fun testBeneficiaryList_Successful() = runTest { + val success = List(5) { mock(Beneficiary::class.java) } + `when`(dataManager.beneficiaryList()).thenReturn(success) + val resultFlow = beneficiaryRepositoryImp.beneficiaryList() + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } + verify(dataManager).beneficiaryList() + } + + @Test + fun testBeneficiaryList_Unsuccessful() = runTest { + `when`(dataManager.beneficiaryList()) + .thenThrow(Exception("Error occurred")) + val result = beneficiaryRepositoryImp.beneficiaryList() + result.test { + assert(Throwable("Error occurred") == awaitError()) + } + } } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt index 094591e95..56285f63d 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/ClientChargeRepositoryImpTest.kt @@ -1,10 +1,12 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking -import okhttp3.ResponseBody +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -12,12 +14,13 @@ import org.junit.runner.RunWith import org.mifos.mobile.api.DataManager import org.mifos.mobile.models.Charge import org.mifos.mobile.models.Page +import org.mifos.mobile.util.checkForUnsuccessfulOperation import org.mockito.Mock -import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner -import retrofit2.Response + @RunWith(MockitoJUnitRunner::class) @ExperimentalCoroutinesApi @@ -38,90 +41,96 @@ class ClientChargeRepositoryImpTest { } @Test - fun testGetClientCharges_Successful() = runBlocking { - val success: Response?> = - Mockito.mock(Response::class.java) as Response?> - - `when`(dataManager.getClientCharges(123L)).thenReturn(success) - - val result = clientChargeRepositoryImp.getClientCharges(123L) - assertEquals(result, success) + fun testGetClientCharges_Successful() = runTest { + val clientChargeMock = List(5) { mock(Charge::class.java)} + val chargeList = clientChargeMock.toList() + val success = Page(5,chargeList) + `when`(dataManager.getClientCharges(123L)) + .thenReturn(success) + val resultFlow = clientChargeRepositoryImp.getClientCharges(123L) + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testGetClientCharges_Unsuccessful() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.getClientCharges(123L)).thenReturn(error) - + @Test(expected = Exception::class) + fun testGetClientCharges_Unsuccessful() = runTest { + `when`(dataManager.getClientCharges(123L)) + .thenThrow( Exception("Error occurred")) val result = clientChargeRepositoryImp.getClientCharges(123L) - assertEquals(result, error) + result.test { + assert(Throwable("Error occurred") == awaitError()) + } + } @Test - fun testGetLoanCharges_Successful() = runBlocking { - val success: Response?> = - Mockito.mock(Response::class.java) as Response?> - + fun testGetLoanCharges_Successful() = runTest { + val loanChargeMock = mock(Charge::class.java) + val success = List(5) { loanChargeMock }.toList() `when`(dataManager.getLoanCharges(123L)).thenReturn(success) - - val result = clientChargeRepositoryImp.getLoanCharges(123L) - assertEquals(result, success) + val resultFlow = clientChargeRepositoryImp.getLoanCharges(123L) + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testGetLoanCharges_Unsuccessful() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.getLoanCharges(123L)).thenReturn(error) - + @Test(expected = Exception::class) + fun testGetLoanCharges_Unsuccessful() = runTest { + `when`(dataManager.getLoanCharges(123L)) + .thenThrow( Exception("Error occurred")) val result = clientChargeRepositoryImp.getLoanCharges(123L) - assertEquals(result, error) + result.test { + assert(Throwable("Error occurred") == awaitError()) + } } @Test - fun testGetSavingsCharges_Successful() = runBlocking { - val success: Response?> = - Mockito.mock(Response::class.java) as Response?> - + fun testGetSavingsCharges_Successful() = runTest { + val savingChargeMock = mock(Charge::class.java) + val success = List(5) { savingChargeMock }.toList() `when`(dataManager.getSavingsCharges(123L)).thenReturn(success) - - val result = clientChargeRepositoryImp.getSavingsCharges(123L) - assertEquals(result, success) + val resultFlow = clientChargeRepositoryImp.getSavingsCharges(123L) + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testGetSavingsCharges_Unsuccessful() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.getSavingsCharges(123L)).thenReturn(error) - + @Test(expected = Exception::class) + fun testGetSavingsCharges_Unsuccessful() = runTest { + `when`(dataManager.getSavingsCharges(123L)) + .thenThrow( Exception("Error occurred")) val result = clientChargeRepositoryImp.getSavingsCharges(123L) - assertEquals(result, error) + result.test { + assert(Throwable("Error occurred") == awaitError()) + } + } @Test - fun testClientLocalCharges_Successful() = runBlocking { - val success: Response?> = - Mockito.mock(Response::class.java) as Response?> - + fun testClientLocalCharges_Successful() = runTest { + val clientLocalChargeMock = List(5) { mock(Charge::class.java)} + val chargeList = clientLocalChargeMock.toList() + val success = Page(5,chargeList) `when`(dataManager.clientLocalCharges()).thenReturn(success) - - val result = clientChargeRepositoryImp.clientLocalCharges() - assertEquals(result, success) + val resultFlow = clientChargeRepositoryImp.clientLocalCharges() + resultFlow.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testClientLocalCharges_Unsuccessful() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.clientLocalCharges()).thenReturn(error) - + @Test(expected = Exception::class) + fun testClientLocalCharges_Unsuccessful() = runTest { + `when`(dataManager.clientLocalCharges()) + .thenThrow( Exception("Error occurred")) val result = clientChargeRepositoryImp.clientLocalCharges() - assertEquals(result, error) + result.test { + assert(Throwable("Error occurred") == awaitError()) + } + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt index bf5cbd859..c85faa6d9 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/ClientRepositoryImpTest.kt @@ -1,8 +1,11 @@ package org.mifos.mobile.repositories +import app.cash.turbine.test import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.Credentials import okhttp3.ResponseBody @@ -14,13 +17,19 @@ import org.mifos.mobile.FakeRemoteDataSource import org.mifos.mobile.api.BaseURL import org.mifos.mobile.api.DataManager import org.mifos.mobile.api.local.PreferencesHelper +import org.mifos.mobile.models.Charge import org.mifos.mobile.models.Page import org.mifos.mobile.models.client.Client +import org.mifos.mobile.util.checkForUnsuccessfulOperation +import org.mockito.ArgumentMatchers.any import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response +import retrofit2.Retrofit @RunWith(MockitoJUnitRunner::class) class ClientRepositoryImpTest { @@ -31,45 +40,59 @@ class ClientRepositoryImpTest { @Mock lateinit var preferencesHelper: PreferencesHelper + @Mock + lateinit var retrofit: Retrofit + + @Mock + lateinit var retrofitBuilder: Retrofit.Builder + private var mockClientPage: Page? = null private lateinit var clientRepositoryImp: ClientRepositoryImp @Before fun setUp() { MockitoAnnotations.openMocks(this) - clientRepositoryImp = ClientRepositoryImp(dataManager, preferencesHelper) + clientRepositoryImp = ClientRepositoryImp( + dataManager, + preferencesHelper, + retrofit + ) mockClientPage = FakeRemoteDataSource.clients } @Test fun testLoadClient_SuccessResponseReceivedFromDataManager_ReturnsClientPageSuccessfully() = - runBlocking { + runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val successResponse: Response?> = Response.success(mockClientPage) + val successResponse:Page + = Page(5, List(5) { + (mock(Client::class.java) as Client) + }) Mockito.`when`( dataManager.clients() ).thenReturn(successResponse) - val result = clientRepositoryImp.loadClient() - - Mockito.verify(dataManager).clients() - Assert.assertEquals(result, successResponse) + val resultFlow = clientRepositoryImp.loadClient() + resultFlow.test { + Assert.assertEquals(successResponse, awaitItem()) + cancelAndIgnoreRemainingEvents() + } + Mockito.verify(dataManager).clients() Dispatchers.resetMain() } - @Test - fun testLoadClient_ErrorResponseReceivedFromDataManager_ReturnsError() = runBlocking{ + @Test(expected = Exception::class) + fun testLoadClient_ErrorResponseReceivedFromDataManager_ReturnsError() =runTest{ Dispatchers.setMain(Dispatchers.Unconfined) - val errorResponse: Response?> = - Response.error(404, ResponseBody.create(null,"error")) - Mockito.`when`( + Mockito.`when`( dataManager.clients() - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = clientRepositoryImp.loadClient() - + result.test{ + assert(Throwable("Error occurred") == awaitError()) + } Mockito.verify(dataManager).clients() - Assert.assertEquals(result, errorResponse) Dispatchers.resetMain() } @@ -77,11 +100,9 @@ class ClientRepositoryImpTest { fun testUpdateAuthenticationToken() { val mockPassword = "testPassword" val mockUsername = "testUsername" - Mockito.`when`(preferencesHelper.userName).thenReturn(mockUsername) - - Mockito.`when`(preferencesHelper.baseUrl) - .thenReturn(BaseURL.PROTOCOL_HTTPS + BaseURL.API_ENDPOINT) - + `when`(preferencesHelper.userName).thenReturn(mockUsername) + `when`(retrofit.newBuilder()).thenReturn(retrofitBuilder) + `when`(retrofitBuilder.client(any())).thenReturn(retrofitBuilder) clientRepositoryImp.updateAuthenticationToken(mockPassword) val authenticationToken = Credentials.basic(preferencesHelper.userName!!, mockPassword) diff --git a/app/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt index b4844965c..af236d4a0 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/GuarantorRepositoryImpTest.kt @@ -1,9 +1,10 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Rule @@ -17,7 +18,6 @@ import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner -import java.io.IOException @RunWith(MockitoJUnitRunner::class) @ExperimentalCoroutinesApi @@ -38,125 +38,157 @@ class GuarantorRepositoryImpTest { } @Test - fun testGetGuarantorTemplate_Successful() = runBlocking { + fun testGetGuarantorTemplate_Successful() = runTest { val success = mock(GuarantorTemplatePayload::class.java) `when`(dataManager.getGuarantorTemplate(123L)).thenReturn(success) val result = guarantorRepositoryImp.getGuarantorTemplate(123L) - + + result.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).getGuarantorTemplate(123L) - assertEquals(result, success) + } - @Test - fun testGetGuarantorTemplate_Unsuccessful() = runBlocking { - val error = IOException("error") + @Test(expected = Exception::class) + fun testGetGuarantorTemplate_Unsuccessful() = runTest { + val error = Exception("error") `when`(dataManager.getGuarantorTemplate(123L)).thenThrow(error) val result = guarantorRepositoryImp.getGuarantorTemplate(123L) - + result.test { + assertEquals(Throwable("error"), awaitError()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).getGuarantorTemplate(123L) - assertEquals(result, error) + } @Test - fun testCreateGuarantor_Successful() = runBlocking { + fun testCreateGuarantor_Successful() = runTest { val success = mock(ResponseBody::class.java) val payload = mock(GuarantorApplicationPayload::class.java) `when`(dataManager.createGuarantor(123L, payload)).thenReturn(success) val result = guarantorRepositoryImp.createGuarantor(123L, payload) - + result.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).createGuarantor(123L, payload) - assertEquals(result, success) + } - @Test - fun testCreateGuarantor_Unsuccessful() = runBlocking { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testCreateGuarantor_Unsuccessful() = runTest { + val error = Exception("Error") val payload = mock(GuarantorApplicationPayload::class.java) `when`(dataManager.createGuarantor(123L, payload)).thenThrow(error) val result = guarantorRepositoryImp.createGuarantor(123L, payload) - + result.test { + assertEquals(Throwable("Error"), awaitError()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).createGuarantor(123L, payload) - assertEquals(result, error) + } @Test - fun testUpdateGuarantor_Successful() = runBlocking { + fun testUpdateGuarantor_Successful() = runTest { val success = mock(ResponseBody::class.java) val payload = mock(GuarantorApplicationPayload::class.java) `when`(dataManager.updateGuarantor(payload, 11L, 22L)).thenReturn(success) val result = guarantorRepositoryImp.updateGuarantor(payload, 11L, 22L) - + result.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).updateGuarantor(payload, 11L, 22L) - assertEquals(result, success) + } - @Test - fun testUpdateGuarantor_Unsuccessful() = runBlocking { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testUpdateGuarantor_Unsuccessful() = runTest { + val error = Exception("Error") val payload = mock(GuarantorApplicationPayload::class.java) `when`(dataManager.updateGuarantor(payload, 11L, 22L)).thenThrow(error) val result = guarantorRepositoryImp.updateGuarantor(payload, 11L, 22L) - + result.test { + assertEquals(Throwable("Error"), awaitError()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).updateGuarantor(payload, 11L, 22L) - assertEquals(result, error) + } @Test - fun testDeleteGuarantor_Successful() = runBlocking { + fun testDeleteGuarantor_Successful() = runTest { val success = mock(ResponseBody::class.java) `when`(dataManager.deleteGuarantor(1L, 2L)).thenReturn(success) val result = guarantorRepositoryImp.deleteGuarantor(1L, 2L) - + result.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).deleteGuarantor(1L, 2L) - assertEquals(result, success) + } - @Test - fun testDeleteGuarantor_Unsuccessful() = runBlocking { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testDeleteGuarantor_Unsuccessful() = runTest { + val error = Exception("Error") `when`(dataManager.deleteGuarantor(1L, 2L)).thenThrow(error) val result = guarantorRepositoryImp.deleteGuarantor(1L, 2L) - + result.test { + assertEquals(Throwable("Error"), awaitError()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).deleteGuarantor(1L, 2L) - assertEquals(result, error) + } @Test - fun testGetGuarantorList_Successful() = runBlocking { - val success = mock(GuarantorPayload::class.java) as List - + fun testGetGuarantorList_Successful() = runTest { + val success = List(4) { + mock(GuarantorPayload::class.java) + } `when`(dataManager.getGuarantorList(123L)).thenReturn(success) val result = guarantorRepositoryImp.getGuarantorList(123L) - + result.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).getGuarantorList(123L) - assertEquals(result, success) + } - @Test - fun testGetGuarantorList_Unsuccessful() = runBlocking { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testGetGuarantorList_Unsuccessful() = runTest { + val error = Exception("Error") `when`(dataManager.getGuarantorList(123L)).thenThrow(error) val result = guarantorRepositoryImp.getGuarantorList(123L) - + result.test { + assertEquals(Throwable("Error"), awaitError()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).getGuarantorList(123L) - assertEquals(result, error) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt index 0cfc27d11..9c45fb72a 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/HomeRepositoryImpTest.kt @@ -1,10 +1,11 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test import junit.framework.Assert.assertEquals import junit.framework.Assert.fail import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Rule @@ -38,125 +39,113 @@ class HomeRepositoryImpTest { } @Test - fun testClientAccounts_Successful() = runBlocking { + fun testClientAccounts_Successful() = runTest { val mockClientAccounts: ClientAccounts = mock(ClientAccounts::class.java) `when`(dataManager.clientAccounts()).thenReturn(mockClientAccounts) val flow = homeRepositoryImp.clientAccounts() - flow.collect { result -> - assertEquals(mockClientAccounts, result) + flow.test { + assertEquals(mockClientAccounts, awaitItem()) + cancelAndIgnoreRemainingEvents() } } @Test - fun testCurrentClient_Successful() = runBlocking { + fun testCurrentClient_Successful() = runTest { val mockClient: Client = mock(Client::class.java) `when`(dataManager.currentClient()).thenReturn(mockClient) val flow = homeRepositoryImp.currentClient() - flow.collect { result -> - assertEquals(mockClient, result) + flow.test { + assertEquals(mockClient, awaitItem()) + cancelAndIgnoreRemainingEvents() } } @Test - fun testClientImage_Successful() = runBlocking { + fun testClientImage_Successful() = runTest { val mockResponseBody: ResponseBody = mock(ResponseBody::class.java) `when`(dataManager.clientImage()).thenReturn(mockResponseBody) val flow = homeRepositoryImp.clientImage() - flow.collect { result -> - assertEquals(mockResponseBody, result) + flow.test { + assertEquals(mockResponseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() } } @Test - fun testUnreadNotificationsCount_Successful() = runBlocking { + fun testUnreadNotificationsCount_Successful() = runTest { val mockUnreadCount = 5 `when`(dataManager.unreadNotificationsCount()).thenReturn(mockUnreadCount) val flow = homeRepositoryImp.unreadNotificationsCount() - flow.collect { result -> - assertEquals(mockUnreadCount, result) + flow.test { + assertEquals(mockUnreadCount, awaitItem()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testClientAccounts_Error() = runBlocking { + @Test(expected = Exception::class) + fun testClientAccounts_Error() = runTest { val errorMessage = "Failed to fetch client accounts" - val mockErrorResponse: ClientAccounts = mock(ClientAccounts::class.java) - - `when`(dataManager.clientAccounts()).thenReturn(mockErrorResponse) - + `when`(dataManager.clientAccounts()). + thenThrow(Exception(errorMessage)) val flow = homeRepositoryImp.clientAccounts() - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testCurrentClient_Error() = runBlocking { + @Test(expected = Exception::class) + fun testCurrentClient_Error() = runTest { val errorMessage = "Failed to fetch current client" - val mockErrorResponse: Client = mock(Client::class.java) - - `when`(dataManager.currentClient()).thenReturn(mockErrorResponse) + `when`(dataManager.currentClient()) + .thenThrow(Exception(errorMessage)) val flow = homeRepositoryImp.currentClient() - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testClientImage_Error() = runBlocking { + @Test(expected = Exception::class) + fun testClientImage_Error() = runTest { val errorMessage = "Failed to fetch client image" - val mockErrorResponse: ResponseBody = mock(ResponseBody::class.java) - - `when`(dataManager.clientImage()).thenReturn(mockErrorResponse) - + `when`(dataManager.clientImage()) + .thenThrow(Exception(errorMessage)) val flow = homeRepositoryImp.clientImage() - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testUnreadNotificationsCount_Error() = runBlocking { + @Test(expected = Exception::class) + fun testUnreadNotificationsCount_Error() = runTest { val errorMessage = "Failed to fetch unread notifications count" - `when`(dataManager.unreadNotificationsCount()).thenReturn(502) + `when`(dataManager.unreadNotificationsCount()) + .thenThrow(Exception(errorMessage)) val flow = homeRepositoryImp.unreadNotificationsCount() - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt index 56b2461db..811ca8d0e 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/LoanRepositoryImpTest.kt @@ -1,9 +1,10 @@ package org.mifos.mobile.repositories +import app.cash.turbine.test import junit.framework.Assert.assertEquals import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.Before @@ -15,6 +16,7 @@ import org.mifos.mobile.models.accounts.loan.LoanWithdraw import org.mifos.mobile.models.templates.loans.LoanTemplate import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.Mockito.`when` import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -38,10 +40,9 @@ class LoanRepositoryImpTest { } @Test - fun testGetLoanWithAssociations_Successful() = runBlocking { + fun testGetLoanWithAssociations_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val success: Response = - Response.success(Mockito.mock(LoanWithAssociations::class.java)) + val success= mock(LoanWithAssociations::class.java) `when`( dataManager.getLoanWithAssociations( @@ -54,108 +55,120 @@ class LoanRepositoryImpTest { "associationType", 1 ) + result?.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).getLoanWithAssociations(Mockito.anyString(), Mockito.anyLong()) - assertEquals(result, success) Dispatchers.resetMain() } - @Test - fun testGetLoanWithAssociations_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testGetLoanWithAssociations_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) `when`( dataManager.getLoanWithAssociations( Mockito.anyString(), Mockito.anyLong() ) - ).thenReturn(error) - + ).thenThrow(Exception("Error occurred")) val result = loanRepositoryImp.getLoanWithAssociations( "associationType", 1 ) - + result!!.test { + assert(Throwable("Error occurred") == awaitError()) + } verify(dataManager).getLoanWithAssociations(Mockito.anyString(), Mockito.anyLong()) - assertEquals(result, error) Dispatchers.resetMain() } @Test - fun testWithdrawLoanAccount_Successful() = runBlocking { + fun testWithdrawLoanAccount_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val success: Response = - Response.success(Mockito.mock(ResponseBody::class.java)) + val success = mock(ResponseBody::class.java) `when`(dataManager.withdrawLoanAccount(1, loanWithdraw)).thenReturn(success) val result = loanRepositoryImp.withdrawLoanAccount(1, loanWithdraw) + result?.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).withdrawLoanAccount(1, loanWithdraw) - assertEquals(result, success) - } + } - @Test - fun testWithdrawLoanAccount_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testWithdrawLoanAccount_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - `when`(dataManager.withdrawLoanAccount(1, loanWithdraw)).thenReturn(error) - + `when`(dataManager.withdrawLoanAccount(1, loanWithdraw)) + .thenThrow(Exception("Error occurred")) val result = loanRepositoryImp.withdrawLoanAccount(1, loanWithdraw) + result!!.test { + assert(Throwable("Error occurred") == awaitError()) + } verify(dataManager).withdrawLoanAccount(1, loanWithdraw) - assertEquals(result, error) Dispatchers.resetMain() } @Test - fun testTemplate_Successful() = runBlocking { + fun testTemplate_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val success: Response = - Response.success(Mockito.mock(LoanTemplate::class.java)) + val success= mock(LoanTemplate::class.java) `when`(dataManager.loanTemplate()).thenReturn(success) val result = loanRepositoryImp.template() + + result?.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).loanTemplate() - assertEquals(result, success) Dispatchers.resetMain() } - @Test - fun testTemplate_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testTemplate_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - `when`(dataManager.loanTemplate()).thenReturn(error) + `when`(dataManager.loanTemplate()) + .thenThrow(Exception("Error occurred")) val result = loanRepositoryImp.template() + result!!.test { + assert(Throwable("Error occurred") == awaitError()) + } verify(dataManager).loanTemplate() - assertEquals(result, error) Dispatchers.resetMain() } @Test - fun testGetLoanTemplateByProduct_Successful() = runBlocking { + fun testGetLoanTemplateByProduct_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val success: Response = - Response.success(Mockito.mock(LoanTemplate::class.java)) + val success = mock(LoanTemplate::class.java) `when`(dataManager.getLoanTemplateByProduct(1)).thenReturn(success) val result = loanRepositoryImp.getLoanTemplateByProduct(1) + result?.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } verify(dataManager).getLoanTemplateByProduct(1) - assertEquals(result, success) Dispatchers.resetMain() } - @Test - fun testGetLoanTemplateByProduct_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testGetLoanTemplateByProduct_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val error: Response = Response.error(404, ResponseBody.create(null, "error")) - `when`(dataManager.getLoanTemplateByProduct(1)).thenReturn(error) + `when`(dataManager.getLoanTemplateByProduct(1)). + thenThrow(Exception("Error occurred")) val result = loanRepositoryImp.getLoanTemplateByProduct(1) + result!!.test { + assert(Throwable("Error occurred") == awaitError()) + } verify(dataManager).getLoanTemplateByProduct(1) - assertEquals(result, error) Dispatchers.resetMain() } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt index 00b940567..eea5d6874 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/NotificationRepositoryImpTest.kt @@ -1,10 +1,12 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -13,6 +15,7 @@ import org.mifos.mobile.api.DataManager import org.mifos.mobile.models.notification.MifosNotification import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner @@ -36,32 +39,30 @@ class NotificationRepositoryImpTest { } @Test - fun testLoadNotifications_SuccessResponseReceivedFromDataManager_ReturnsSuccess() = runBlocking { - val notificationList : List = ArrayList() + fun testLoadNotifications_SuccessResponseReceivedFromDataManager_ReturnsSuccess() = runTest { + val notification = mock(MifosNotification::class.java) + val notificationList = List(5){ notification} Mockito.`when`( dataManager.notifications() - ).thenReturn(flowOf(notificationList)) + ).thenReturn(notificationList) val notifications = notificationRepositoryImp.loadNotifications() - notifications.collect { result -> - assert(result == notificationList) + notifications.test { + assertEquals(notificationList, awaitItem()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testLoadNotifications_ErrorResponseReceivedFromDataManager_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testLoadNotifications_ErrorResponseReceivedFromDataManager_ReturnsError() = runTest { val dummyError = Exception("Dummy error") `when`(dataManager.notifications()).thenThrow(dummyError) val notifications = notificationRepositoryImp.loadNotifications() - try { - notifications.catch { exception -> - assert(exception == dummyError) - } - } catch (e: Exception) { - assert(e == dummyError) + notifications.test { + assert(Throwable("Dummy error") == awaitItem()) } } diff --git a/app/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt index 6795ce9ae..636a1b3cd 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/RecentTransactionRepositoryImpTest.kt @@ -1,8 +1,10 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Assert import org.junit.Before @@ -14,6 +16,7 @@ import org.mifos.mobile.models.Page import org.mifos.mobile.models.Transaction import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response @@ -38,33 +41,35 @@ class RecentTransactionRepositoryImpTest { } @Test - fun recentTransaction_successful_response_from_dataManger() = runBlocking { - val success: Response?> = - Response.success(Mockito.mock(Page()::class.java)) + fun recentTransaction_successful_response_from_dataManger() = runTest { + val success= mock(Page()::class.java) val offset = 0 val limit = 50 Mockito.`when`(dataManager.getRecentTransactions(offset, limit)).thenReturn(success) val result = recentTransactionRepositoryImp.recentTransactions(offset, limit) - + result.test { + assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).getRecentTransactions(offset, limit) - Assert.assertEquals(result, success) } - @Test - fun recentTransaction_unsuccessful_response_from_dataManger() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) + @Test(expected = Exception::class) + fun recentTransaction_unsuccessful_response_from_dataManger() = runTest { val offset = 0 val limit = 50 - Mockito.`when`(dataManager.getRecentTransactions(offset, limit)).thenReturn(error) + Mockito.`when`(dataManager.getRecentTransactions(offset, limit)) + .thenThrow(Exception("Error Occured in fetching recent transactions")) val result = recentTransactionRepositoryImp.recentTransactions(offset, limit) - + result.test { + assertEquals(Throwable("Error Occured in fetching recent transactions"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).getRecentTransactions(offset, limit) - Assert.assertEquals(result, error) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt index e9ffdafc5..b5776d449 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/SavingsAccountRepositoryImpTest.kt @@ -1,8 +1,10 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Assert import org.junit.Before @@ -19,6 +21,7 @@ import org.mifos.mobile.models.templates.savings.SavingsAccountTemplate import org.mifos.mobile.utils.Constants import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response @@ -46,9 +49,8 @@ class SavingsAccountRepositoryImpTest { @Test fun testGetSavingsWithAssociations_SuccessResponseReceivedFromDataManager_ReturnsSuccess() = - runBlocking { - val mockSavingsWithAssociations: Response = - Response.success(Mockito.mock(SavingsWithAssociations::class.java)) + runTest { + val mockSavingsWithAssociations = mock(SavingsWithAssociations::class.java) Mockito.`when`( dataManager.getSavingsWithAssociations(mockAccountId, mockAssociationType) ).thenReturn(mockSavingsWithAssociations) @@ -57,70 +59,73 @@ class SavingsAccountRepositoryImpTest { mockAccountId, mockAssociationType ) - + result.test { + Assert.assertEquals(mockSavingsWithAssociations, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .getSavingsWithAssociations(mockAccountId, mockAssociationType) - Assert.assertEquals(result, mockSavingsWithAssociations) } - @Test + @Test(expected = Exception::class) fun testGetSavingsWithAssociations_ErrorResponseReceivedFromDataManager_ReturnsError() = - runBlocking { - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) + runTest { Mockito.`when`( dataManager.getSavingsWithAssociations(mockAccountId, mockAssociationType) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = savingsAccountRepositoryImp.getSavingsWithAssociations( mockAccountId, mockAssociationType ) - + result.test { + assertEquals(Throwable("Error occurred"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .getSavingsWithAssociations(mockAccountId, mockAssociationType) - Assert.assertEquals(result, errorResponse) + } @Test fun testGetSavingsAccountApplicationTemplate_SuccessResponseFromDataManager_ReturnsSuccess() = - runBlocking { - val mockSavingsAccountTemplate: Response = - Response.success(Mockito.mock(SavingsAccountTemplate::class.java)) + runTest { + val mockSavingsAccountTemplate = mock(SavingsAccountTemplate::class.java) Mockito.`when`( dataManager.getSavingAccountApplicationTemplate(mockClientId) ).thenReturn(mockSavingsAccountTemplate) val result = savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId) - + result.test { + assertEquals(mockSavingsAccountTemplate, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).getSavingAccountApplicationTemplate(mockClientId) - Assert.assertEquals(result, mockSavingsAccountTemplate) } - @Test + @Test(expected = Exception::class) fun testGetSavingsAccountApplicationTemplate_ErrorResponseFromDataManager_ReturnsError() = - runBlocking { - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) + runTest { Mockito.`when`( dataManager.getSavingAccountApplicationTemplate(mockClientId) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId) - + result.test { + assertEquals(Throwable("Error occurred"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).getSavingAccountApplicationTemplate(mockClientId) - Assert.assertEquals(result, errorResponse) } @Test fun testSubmitSavingAccountApplication_SuccessResponseFromDataManager_ReturnsSuccess() = - runBlocking { + runTest { val mockSavingsAccountApplicationPayload = Mockito.mock(SavingsAccountApplicationPayload::class.java) - val responseBody: Response = - Response.success(Mockito.mock(ResponseBody::class.java)) + val responseBody = mock(ResponseBody::class.java) Mockito.`when`( dataManager.submitSavingAccountApplication(mockSavingsAccountApplicationPayload) ).thenReturn(responseBody) @@ -128,37 +133,40 @@ class SavingsAccountRepositoryImpTest { val result = savingsAccountRepositoryImp.submitSavingAccountApplication( mockSavingsAccountApplicationPayload ) - + result.test { + assertEquals(responseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .submitSavingAccountApplication(mockSavingsAccountApplicationPayload) - Assert.assertEquals(result, responseBody) } - @Test + @Test(expected = Exception::class) fun testSubmitSavingAccountApplication_ErrorResponseFromDataManager_ReturnsError() = - runBlocking { + runTest { val mockSavingsAccountApplicationPayload = Mockito.mock(SavingsAccountApplicationPayload::class.java) - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) Mockito.`when`( dataManager.submitSavingAccountApplication(mockSavingsAccountApplicationPayload) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = savingsAccountRepositoryImp.submitSavingAccountApplication( mockSavingsAccountApplicationPayload ) + result.test { + assertEquals(Throwable("Error occurred"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .submitSavingAccountApplication(mockSavingsAccountApplicationPayload) - Assert.assertEquals(result, errorResponse) + } @Test - fun testUpdateSavingsAccount_SuccessResponseFromDataManager_ReturnsSuccess() = runBlocking { + fun testUpdateSavingsAccount_SuccessResponseFromDataManager_ReturnsSuccess() = runTest { val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java) - val responseBody: Response = - Response.success(Mockito.mock(ResponseBody::class.java)) + val responseBody = mock(ResponseBody::class.java) Mockito.`when`( dataManager.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload) ).thenReturn(responseBody) @@ -167,39 +175,41 @@ class SavingsAccountRepositoryImpTest { mockAccountId, mockSavingsAccountUpdatePayload ) - + result.test { + assertEquals(responseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload) - Assert.assertEquals(result, responseBody) } - @Test - fun testUpdateSavingsAccount_ErrorResponseFromDataManager_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testUpdateSavingsAccount_ErrorResponseFromDataManager_ReturnsError() = runTest { val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java) - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) Mockito.`when`( dataManager.updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = savingsAccountRepositoryImp.updateSavingsAccount( mockAccountId, mockSavingsAccountUpdatePayload ) - + result.test { + assertEquals(Throwable("Error occurred"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .updateSavingsAccount(mockAccountId, mockSavingsAccountUpdatePayload) - Assert.assertEquals(result, errorResponse) + } @Test fun testSubmitWithdrawSavingsAccount_SuccessResponseFromDataManager_ReturnsSuccess() = - runBlocking { + runTest { val mockAccountId = "1" val mockSavingsAccountWithdrawPayload = Mockito.mock(SavingsAccountWithdrawPayload::class.java) - val responseBody: Response = - Response.success(Mockito.mock(ResponseBody::class.java)) + val responseBody = mock(ResponseBody::class.java) Mockito.`when`( dataManager.submitWithdrawSavingsAccount( mockAccountId, @@ -211,62 +221,68 @@ class SavingsAccountRepositoryImpTest { mockAccountId, mockSavingsAccountWithdrawPayload ) + result.test { + assertEquals(responseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .submitWithdrawSavingsAccount(mockAccountId, mockSavingsAccountWithdrawPayload) - Assert.assertEquals(result, responseBody) } - @Test - fun testSubmitWithdrawSavingsAccount_ErrorResponseFromDataManager_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testSubmitWithdrawSavingsAccount_ErrorResponseFromDataManager_ReturnsError() = runTest { val mockAccountId = "1" val mockSavingsAccountWithdrawPayload = Mockito.mock(SavingsAccountWithdrawPayload::class.java) - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) - Mockito.`when`( + Mockito.`when`( dataManager.submitWithdrawSavingsAccount( mockAccountId, mockSavingsAccountWithdrawPayload ) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = savingsAccountRepositoryImp.submitWithdrawSavingsAccount( mockAccountId, mockSavingsAccountWithdrawPayload ) - + result.test { + assertEquals(Throwable("Error occurred"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager) .submitWithdrawSavingsAccount(mockAccountId, mockSavingsAccountWithdrawPayload) - Assert.assertEquals(result, errorResponse) + } @Test fun testLoanAccountTransferTemplate_SuccessResponseFromDataManager_ReturnsSuccess() = - runBlocking { - val responseBody: Response = - Response.success(Mockito.mock(AccountOptionsTemplate::class.java)) + runTest { + val responseBody=mock(AccountOptionsTemplate::class.java) Mockito.`when`( dataManager.accountTransferTemplate() ).thenReturn(responseBody) val result = savingsAccountRepositoryImp.loanAccountTransferTemplate() - + result.test { + assertEquals(responseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).accountTransferTemplate() - Assert.assertEquals(result, responseBody) } - @Test - fun testLoanAccountTransferTemplate_ErrorResponseFromDataManager_ReturnsError() = runBlocking { - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) - Mockito.`when`( + @Test(expected = Exception::class) + fun testLoanAccountTransferTemplate_ErrorResponseFromDataManager_ReturnsError() = runTest { + Mockito.`when`( dataManager.accountTransferTemplate() - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = savingsAccountRepositoryImp.loanAccountTransferTemplate() - + result.test { + assertEquals(Throwable("Error occurred"), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).accountTransferTemplate() - Assert.assertEquals(result, errorResponse) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt index a5ad341f5..bf9f68ed2 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/ThirdPartyTransferRepositoryImpTest.kt @@ -1,9 +1,10 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Rule @@ -38,50 +39,27 @@ class ThirdPartyTransferRepositoryImpTest { } @Test - fun testThirdPartyTransferTemplate_Successful() = runBlocking { - val response: Response = - Response.success(Mockito.mock(AccountOptionsTemplate::class.java)) + fun testThirdPartyTransferTemplate_Successful() = runTest { + val response= Mockito.mock(AccountOptionsTemplate::class.java) `when`(dataManager.thirdPartyTransferTemplate()).thenReturn(response) val result = transferRepositoryImp.thirdPartyTransferTemplate() - - assertEquals(result, response) + result.test { + assertEquals(response, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testThirdPartyTransferTemplate_Unsuccessful() = runBlocking { - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - `when`(dataManager.thirdPartyTransferTemplate()).thenReturn(error) - + @Test(expected = Exception::class) + fun testThirdPartyTransferTemplate_Unsuccessful() = runTest { + `when`(dataManager.thirdPartyTransferTemplate()) + .thenThrow(Exception("Error")) val result = transferRepositoryImp.thirdPartyTransferTemplate() - - assertEquals(result, error) + result.test { + assertEquals(Throwable("Error"), awaitError()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testBeneficiaryList_Successful() = runBlocking { - val list1 = Mockito.mock(Beneficiary::class.java) - val list2 = Mockito.mock(Beneficiary::class.java) - - val response: Response?> = Response.success(listOf(list1, list2)) - - `when`(dataManager.beneficiaryList()).thenReturn(response) - - val result = transferRepositoryImp.beneficiaryList() - assertEquals(result, response) - } - - @Test - fun testBeneficiaryList_Unsuccessful() = runBlocking { - val error: Response?> = - Response.error(404, ResponseBody.create(null, "error")) - - `when`(dataManager.beneficiaryList()).thenReturn(error) - - val result = transferRepositoryImp.beneficiaryList() - - assertEquals(result, error) - } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt index a0876366b..2a810f774 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/TransferRepositoryImpTest.kt @@ -1,8 +1,9 @@ package org.mifos.mobile.repositories +import app.cash.turbine.test import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.Assert @@ -34,9 +35,9 @@ class TransferRepositoryImpTest { } @Test - fun makeThirdPartyTransfer_successful() = runBlocking { + fun makeThirdPartyTransfer_successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val success = Response.success(Mockito.mock(ResponseBody::class.java)) + val success = Mockito.mock(ResponseBody::class.java) val transferPayload = TransferPayload().apply { this.fromOfficeId = 1 this.fromClientId = 2 @@ -55,7 +56,8 @@ class TransferRepositoryImpTest { this.toAccountNumber = "0000002" } - Mockito.`when`(dataManager.makeThirdPartyTransfer(transferPayload)).thenReturn(success) + Mockito.`when`(dataManager.makeThirdPartyTransfer(transferPayload)) + .thenReturn(success) val result = transferProcessImp.makeTransfer( transferPayload.fromOfficeId, @@ -75,16 +77,18 @@ class TransferRepositoryImpTest { transferPayload.toAccountNumber, TransferType.TPT ) - + result.test { + Assert.assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).makeThirdPartyTransfer(transferPayload) - Assert.assertEquals(result, success) Dispatchers.resetMain() } @Test - fun makeSavingsTransfer_successful() = runBlocking { + fun makeSavingsTransfer_successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val success = Response.success(Mockito.mock(ResponseBody::class.java)) + val success = Mockito.mock(ResponseBody::class.java) val transferPayload = TransferPayload().apply { this.fromOfficeId = 1 this.fromClientId = 2 @@ -123,16 +127,17 @@ class TransferRepositoryImpTest { transferPayload.toAccountNumber, TransferType.SELF ) - + result.test { + Assert.assertEquals(success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).makeTransfer(transferPayload) - Assert.assertEquals(result, success) Dispatchers.resetMain() } - @Test - fun makeThirdPartyTransfer_unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun makeThirdPartyTransfer_unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val error: Response = Response.error(404, ResponseBody.create(null, "error")) val transferPayload = TransferPayload().apply { this.fromOfficeId = 1 this.fromClientId = 2 @@ -150,7 +155,8 @@ class TransferRepositoryImpTest { this.fromAccountNumber = "0000001" this.toAccountNumber = "0000002" } - Mockito.`when`(dataManager.makeThirdPartyTransfer(transferPayload)).thenReturn(error) + Mockito.`when`(dataManager.makeThirdPartyTransfer(transferPayload)) + .thenThrow(Exception("Error occurred")) val result = transferProcessImp.makeTransfer( transferPayload.fromOfficeId, @@ -170,16 +176,17 @@ class TransferRepositoryImpTest { transferPayload.toAccountNumber, TransferType.TPT ) - + result.test { + Assert.assertEquals(Throwable("Error occurred"), awaitError()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).makeThirdPartyTransfer(transferPayload) - Assert.assertEquals(result, error) Dispatchers.resetMain() } - @Test - fun makeSavingsTransfer_unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun makeSavingsTransfer_unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val error: Response = Response.error(404, ResponseBody.create(null, "error")) val transferPayload = TransferPayload().apply { this.fromOfficeId = 1 this.fromClientId = 2 @@ -197,7 +204,8 @@ class TransferRepositoryImpTest { this.fromAccountNumber = "0000001" this.toAccountNumber = "0000002" } - Mockito.`when`(dataManager.makeTransfer(transferPayload)).thenReturn(error) + Mockito.`when`(dataManager.makeTransfer(transferPayload)). + thenThrow(Exception("Error occurred")) val result = transferProcessImp.makeTransfer( transferPayload.fromOfficeId, @@ -217,9 +225,11 @@ class TransferRepositoryImpTest { transferPayload.toAccountNumber, TransferType.SELF ) - + result.test { + Assert.assertEquals(Throwable("Error occurred"), awaitError()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).makeTransfer(transferPayload) - Assert.assertEquals(result, error) Dispatchers.resetMain() } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt index cc91c1fd8..c8d12486c 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/UserAuthRepositoryImpTest.kt @@ -1,8 +1,11 @@ package org.mifos.mobile.repositories import CoroutineTestRule +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Assert import org.junit.Before @@ -17,6 +20,7 @@ import org.mifos.mobile.models.payload.LoginPayload import org.mifos.mobile.models.register.RegisterPayload import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response @@ -44,9 +48,8 @@ class UserAuthRepositoryImpTest { @Test fun testRegisterUser_SuccessResponseReceivedFromDataManager_ReturnSuccessfulRegistration() = - runBlocking { - val successResponse: Response = - Response.success(Mockito.mock(ResponseBody::class.java)) + runTest{ + val successResponse= mock(ResponseBody::class.java) val registerPayload = RegisterPayload().apply { this.accountNumber = "accountNumber" this.authenticationMode = "authenticationMode" @@ -70,17 +73,18 @@ class UserAuthRepositoryImpTest { registerPayload.password, registerPayload.username ) - + result.test { + assertEquals(successResponse, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).registerUser(registerPayload) - Assert.assertEquals(result, successResponse) + } - @Test + @Test(expected = Exception::class) fun testRegisterUser_ErrorResponseReceivedFromDataManager_ReturnsUnsuccessfulRegistration() = - runBlocking { - val error: Response = - Response.error(404, ResponseBody.create(null, "error")) - val registerPayload = RegisterPayload().apply { + runTest{ + val registerPayload = RegisterPayload().apply { this.accountNumber = "accountNumber" this.authenticationMode = "authenticationMode" this.email = "email" @@ -91,7 +95,8 @@ class UserAuthRepositoryImpTest { this.username = "username" } - Mockito.`when`(dataManager.registerUser(registerPayload)).thenReturn(error) + Mockito.`when`(dataManager.registerUser(registerPayload)) + .thenThrow(Exception("Error occurred")) val result = userAuthRepositoryImp.registerUser( registerPayload.accountNumber, @@ -103,50 +108,56 @@ class UserAuthRepositoryImpTest { registerPayload.password, registerPayload.username ) - + result.test { + assertEquals(Throwable("Error occurred"), awaitError()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).registerUser(registerPayload) - Assert.assertEquals(result, error) } @Test - fun testLogin_SuccessResponseReceivedFromDataManager_ReturnsUserSuccessfully() = runBlocking { + fun testLogin_SuccessResponseReceivedFromDataManager_ReturnsUserSuccessfully() = runTest{ val mockLoginPayload = LoginPayload().apply { this.username = "username" this.password = "password" } - val successResponse: Response = Response.success(mockUser) + Mockito.`when`( dataManager.login(mockLoginPayload) - ).thenReturn(successResponse) + ).thenReturn(mockUser) val result = userAuthRepositoryImp.login("username", "password") - + + result.test { + assertEquals(mockUser, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).login(mockLoginPayload) - Assert.assertEquals(result, successResponse) + } - @Test - fun testLogin_ErrorResponseReceivedFromDataManager_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testLogin_ErrorResponseReceivedFromDataManager_ReturnsError() = runTest{ val mockLoginPayload = LoginPayload().apply { this.username = "username" this.password = "password" } - val errorResponse: Response = Response.error(404, ResponseBody.create(null, "error")) - Mockito.`when`( + Mockito.`when`( dataManager.login(mockLoginPayload) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = userAuthRepositoryImp.login("username", "password") - + result.test { + assertEquals(Throwable("Error occurred"), awaitError()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).login(mockLoginPayload) - Assert.assertEquals(result, errorResponse) } @Test fun testVerifyUser_SuccessResponseReceivedFromDataManager_ReturnsSuccessfulRegistrationVerification() = - runBlocking { - val successResponse: Response = - Response.success(Mockito.mock(ResponseBody::class.java)) + runTest{ + val successResponse= mock(ResponseBody::class.java) Mockito.`when`( dataManager.verifyUser(userVerify) ).thenReturn(successResponse) @@ -156,26 +167,30 @@ class UserAuthRepositoryImpTest { userVerify.authenticationToken, userVerify.requestId ) - + result.test { + assertEquals(successResponse, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).verifyUser(userVerify) - Assert.assertEquals(result, successResponse) } - @Test + @Test(expected = Exception::class) fun testVerifyUser_ErrorResponseReceivedFromDataManager_ReturnsUnsuccessfulRegistrationVerification() = - runBlocking { - val errorResponse: Response = - Response.error(404, ResponseBody.create(null, "error")) - Mockito.`when`( + runTest{ + Mockito.`when`( dataManager.verifyUser(userVerify) - ).thenReturn(errorResponse) + ).thenThrow(Exception("Error occurred")) val result = userAuthRepositoryImp.verifyUser( userVerify.authenticationToken, userVerify.requestId ) + result.test { + assert(Throwable("Error occurred") == awaitError()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(dataManager).verifyUser(userVerify) - Assert.assertEquals(result, errorResponse) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt b/app/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt index fc0f06798..a9cca802f 100644 --- a/app/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt +++ b/app/src/test/java/org/mifos/mobile/repositories/UserDetailRepositoryImpTest.kt @@ -1,8 +1,10 @@ package org.mifos.mobile.repositories +import app.cash.turbine.test import junit.framework.Assert.assertEquals import junit.framework.Assert.fail import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Test @@ -28,41 +30,39 @@ class UserDetailRepositoryImpTest { } @Test - fun testRegisterNotification_Success() = runBlocking { + fun testRegisterNotification_Success() = runTest { val mockResponseBody: ResponseBody = mock(ResponseBody::class.java) val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java) `when`(mockDataManager.registerNotification(mockPayload)).thenReturn(mockResponseBody) val flow = userDetailRepository.registerNotification(mockPayload) - flow.collect { result -> - assertEquals(mockResponseBody, result) + flow.test { + assertEquals(mockResponseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testRegisterNotification_Error() = runBlocking { + @Test(expected = Exception::class) + fun testRegisterNotification_Error() = runTest { val errorMessage = "Failed to register notification" val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java) `when`(mockDataManager.registerNotification(mockPayload)).thenThrow( - RuntimeException( + Exception( errorMessage ) ) val flow = userDetailRepository.registerNotification(mockPayload) - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } } @Test - fun testGetUserNotificationId_Success() = runBlocking { + fun testGetUserNotificationId_Success() = runTest { val mockNotificationUserDetail: NotificationUserDetail = mock(NotificationUserDetail::class.java) val mockId = 123L @@ -70,34 +70,32 @@ class UserDetailRepositoryImpTest { val flow = userDetailRepository.getUserNotificationId(mockId) - flow.collect { result -> - assertEquals(mockNotificationUserDetail, result) + flow.test { + assertEquals(mockNotificationUserDetail, awaitItem()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testGetUserNotificationId_Error() = runBlocking { + @Test(expected = Exception::class) + fun testGetUserNotificationId_Error() = runTest { val errorMessage = "Failed to get user notification" val mockId = 123L `when`(mockDataManager.getUserNotificationId(mockId)).thenThrow( - RuntimeException( + Exception( errorMessage ) ) val flow = userDetailRepository.getUserNotificationId(mockId) - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } } @Test - fun testUpdateRegisterNotification_Success() = runBlocking { + fun testUpdateRegisterNotification_Success() = runTest { val mockResponseBody: ResponseBody = mock(ResponseBody::class.java) val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java) val mockId = 123L @@ -107,29 +105,27 @@ class UserDetailRepositoryImpTest { val flow = userDetailRepository.updateRegisterNotification(mockId, mockPayload) - flow.collect { result -> - assertEquals(mockResponseBody, result) + flow.test { + assertEquals(mockResponseBody, awaitItem()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testUpdateRegisterNotificationError() = runBlocking { + @Test(expected = Exception::class) + fun testUpdateRegisterNotificationError() = runTest { val errorMessage = "Failed to update register notification" val mockPayload: NotificationRegisterPayload = mock(NotificationRegisterPayload::class.java) val mockId = 123L `when`(mockDataManager.updateRegisterNotification(mockId, mockPayload)).thenThrow( - RuntimeException(errorMessage) + Exception(errorMessage) ) val flow = userDetailRepository.updateRegisterNotification(mockId, mockPayload) - try { - flow.collect { - fail("Expected an exception") - } - } catch (e: Exception) { - assertEquals(errorMessage, e.message) + flow.test { + assertEquals(Throwable(errorMessage), awaitError()) + cancelAndIgnoreRemainingEvents() } - } + } } diff --git a/app/src/test/java/org/mifos/mobile/util/CheckUnSucess.kt b/app/src/test/java/org/mifos/mobile/util/CheckUnSucess.kt new file mode 100644 index 000000000..f2c4d37da --- /dev/null +++ b/app/src/test/java/org/mifos/mobile/util/CheckUnSucess.kt @@ -0,0 +1,16 @@ +package org.mifos.mobile.util + +import kotlinx.coroutines.flow.Flow +import java.io.IOException + +suspend fun checkForUnsuccessfulOperation(result: Flow): Boolean { + val resultList = mutableListOf() + var errorOccurred = false + try { + result.collect { resultList.add(it) } + } catch (e: RuntimeException) { + errorOccurred = true + // Handle the error (optional) + } + return errorOccurred +} \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt index 70b69b9ed..8da70451f 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt @@ -2,9 +2,16 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import app.cash.turbine.test +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import org.junit.Assert.* import org.junit.Before import org.junit.Rule @@ -54,50 +61,57 @@ class AccountsViewModelTest { } @Test - fun loadClientAccounts_Success() = - runBlocking { - val mockClientAccounts = mock(ClientAccounts::class.java) - `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mockClientAccounts)) - + fun loadClientAccounts_Success() = runTest { + val mockClientAccounts = mock(ClientAccounts::class.java) + `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mockClientAccounts)) + accountsViewModel.accountsUiState.test { accountsViewModel.loadClientAccounts() - + assertEquals(AccountsUiState.Loading, awaitItem()) assertEquals( - AccountsUiState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), - accountsViewModel.accountsUiState.value + AccountsUiState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem() ) assertEquals( - AccountsUiState.ShowLoanAccounts(mockClientAccounts.loanAccounts), - accountsViewModel.accountsUiState.value + AccountsUiState.ShowLoanAccounts(mockClientAccounts.loanAccounts), awaitItem() ) assertEquals( - AccountsUiState.ShowShareAccounts(mockClientAccounts.shareAccounts), - accountsViewModel.accountsUiState.value + AccountsUiState.ShowShareAccounts(mockClientAccounts.shareAccounts), awaitItem() ) + cancelAndIgnoreRemainingEvents() } + } @Test - fun loadAccountsUiState_Success() = runBlocking { - val mockAccountType = "savings" + fun loadAccountsUiState_Success() = runTest { + val mockAccountType = "savingsAccounts" val mockClientAccounts = mock(ClientAccounts::class.java) - `when`(accountsRepositoryImp.loadAccounts(anyString())).thenReturn(flowOf(mockClientAccounts)) - - accountsViewModel.loadAccounts(mockAccountType) - - assertEquals( - AccountsUiState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), - accountsViewModel.accountsUiState.value + `when`(accountsRepositoryImp.loadAccounts(mockAccountType)).thenReturn( + flowOf( + mockClientAccounts + ) ) - + accountsViewModel.accountsUiState.test { + accountsViewModel.loadAccounts(mockAccountType) + assertEquals(AccountsUiState.Loading, awaitItem()) + assertEquals( + AccountsUiState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem() + ) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun loadAccounts_Error() = runBlocking { + @Test(expected = RuntimeException::class) + fun loadAccounts_Error() = runTest { val mockAccountType = "savings" `when`(accountsRepositoryImp.loadAccounts(anyString())).thenThrow(RuntimeException()) - - accountsViewModel.loadAccounts(mockAccountType) - - assertEquals(AccountsUiState.Error, accountsViewModel.accountsUiState.value) + accountsViewModel.accountsUiState.test { + try { + accountsViewModel.loadAccounts(mockAccountType) + assertEquals(AccountsUiState.Loading, awaitItem()) + } catch (e: RuntimeException) { + assertEquals(AccountsUiState.Error, awaitItem()) + } + cancelAndIgnoreRemainingEvents() + } } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt index 616edd49d..c2da54c2d 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/AddGuarantorViewModelTest.kt @@ -3,9 +3,17 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Rule @@ -16,6 +24,7 @@ import org.mifos.mobile.models.guarantor.GuarantorTemplatePayload import org.mifos.mobile.repositories.GuarantorRepositoryImp import org.mifos.mobile.ui.enums.GuarantorState import org.mifos.mobile.util.RxSchedulersOverrideRule +import org.mifos.mobile.utils.BeneficiaryUiState import org.mifos.mobile.utils.GuarantorUiState import org.mockito.Mock import org.mockito.Mockito.* @@ -50,81 +59,96 @@ class AddGuarantorViewModelTest { MockitoAnnotations.openMocks(this) viewModel = AddGuarantorViewModel(guarantorRepositoryImp) } - + fun TestScope.obserrveUiState(): MutableList { + val uiStates = mutableListOf() + viewModel.guarantorUiState.onEach { + println(it) + uiStates.add(it) + }.launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler))) + return uiStates + } @Test - fun testGetGuarantorTemplate_Successful() = runBlocking { + fun testGetGuarantorTemplate_Successful() = runTest { val response = mock(GuarantorTemplatePayload::class.java) `when`(guarantorRepositoryImp.getGuarantorTemplate(123L)).thenReturn( flowOf(response) ) - - viewModel.getGuarantorTemplate(GuarantorState.UPDATE, 123L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged( - GuarantorUiState.ShowGuarantorUpdation( - response - ) - ) - - viewModel.getGuarantorTemplate(GuarantorState.CREATE, 123L) - verify(guarantorUiStateObserver).onChanged( - GuarantorUiState.ShowGuarantorApplication( - response + viewModel.guarantorUiState.test { + viewModel.getGuarantorTemplate(GuarantorState.UPDATE, 123L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals( + GuarantorUiState.ShowGuarantorUpdation( + response + ), + awaitItem() ) - ) + viewModel.getGuarantorTemplate(GuarantorState.CREATE, 123L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals( + GuarantorUiState.ShowGuarantorApplication( + response + ), + awaitItem() + ) + } } - @Test - fun testGetGuarantorTemplate_Unsuccessful() = runBlocking { - val error = IOException("Error") - - `when`(guarantorRepositoryImp.getGuarantorTemplate(123L)).thenThrow(error) - - viewModel.getGuarantorTemplate(GuarantorState.CREATE, 123L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.ShowError(Throwable().message)) - + @Test(expected = Exception::class) + fun testGetGuarantorTemplate_Unsuccessful() = runTest { + `when`(guarantorRepositoryImp.getGuarantorTemplate(123L)) + .thenThrow( Exception("Error occurred")) + viewModel.guarantorUiState.test { + try { + viewModel.getGuarantorTemplate(GuarantorState.UPDATE, 123L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + } catch (e: Exception) { + assertEquals( + GuarantorUiState.ShowError(Throwable().message), + awaitItem() + ) + } + } } @Test - fun testCreateGuarantor_Successful() = runBlocking { + fun testCreateGuarantor_Successful() = runTest { val payload = mock(GuarantorApplicationPayload::class.java) val response = mock(ResponseBody::class.java) - `when`(guarantorRepositoryImp.createGuarantor(123L, payload)).thenReturn( flowOf(response) ) - - viewModel.createGuarantor(123L, payload) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged( - GuarantorUiState.SubmittedSuccessfully( - response.string(), - payload + viewModel.guarantorUiState.test { + viewModel.createGuarantor(123L, payload) + assertEquals(GuarantorUiState.Loading,awaitItem()) + assertEquals( + GuarantorUiState.SubmittedSuccessfully( + response.string(), + payload + ), + awaitItem() ) - ) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testCreateGuarantor_Unsuccessful() { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testCreateGuarantor_Unsuccessful() = runTest { val payload = mock(GuarantorApplicationPayload::class.java) - - `when`(guarantorRepositoryImp.createGuarantor(123L, payload)).thenThrow( - error - ) - - viewModel.createGuarantor(123L, payload) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.ShowError(Throwable().message)) + `when`(guarantorRepositoryImp.createGuarantor(123L, payload)) + .thenThrow( Exception("Error occurred")) + viewModel.guarantorUiState.test { + viewModel.createGuarantor(123L, payload) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals(GuarantorUiState.ShowError(Throwable().message), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @Test - fun testUpdateGuarantor_Successful() { + fun testUpdateGuarantor_Successful() = runTest{ val payload = mock(GuarantorApplicationPayload::class.java) val response = mock(ResponseBody::class.java) - `when`( guarantorRepositoryImp.updateGuarantor( payload, @@ -132,26 +156,30 @@ class AddGuarantorViewModelTest { 22L ) ).thenReturn(flowOf(response)) - - viewModel.updateGuarantor(payload, 11L, 22L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged( - GuarantorUiState.GuarantorUpdatedSuccessfully( - response.string() + viewModel.guarantorUiState.test { + viewModel.updateGuarantor(payload, 11L, 22L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals( + GuarantorUiState.GuarantorUpdatedSuccessfully( + response.string() + ), + awaitItem() ) - ) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testUpdateGuarantor_Unsuccessful() { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testUpdateGuarantor_Unsuccessful() = runTest { val payload = mock(GuarantorApplicationPayload::class.java) - - `when`(guarantorRepositoryImp.updateGuarantor(payload, 11L, 22L)).thenThrow(error) - - viewModel.updateGuarantor(payload, 11L, 22L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.ShowError(Throwable().message)) + `when`(guarantorRepositoryImp.updateGuarantor(payload, 11L, 22L)) + .thenThrow( Exception("Error occurred")) + viewModel.guarantorUiState.test { + viewModel.updateGuarantor(payload, 11L, 22L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals(GuarantorUiState.ShowError(Throwable().message), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt index facbb5d96..09be577e8 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryApplicationViewModelTest.kt @@ -4,8 +4,25 @@ import CoroutineTestRule import android.view.View import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertTrue +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.createTestCoroutineScope +import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -47,76 +64,104 @@ class BeneficiaryApplicationViewModelTest { private lateinit var viewModel: BeneficiaryApplicationViewModel + // Test coroutine dispatcher and scope + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = BeneficiaryApplicationViewModel(beneficiaryRepositoryImp) - viewModel.beneficiaryUiState.observeForever(beneficiaryUiStateObserver) } @Test - fun testLoadBeneficiaryTemplate_Successful() = runBlocking { + fun testLoadBeneficiaryTemplate_Successful() = runTest { val response = mock(BeneficiaryTemplate::class.java) - `when`(beneficiaryRepositoryImp.beneficiaryTemplate()).thenReturn(Response.success(response)) - - viewModel.loadBeneficiaryTemplate() - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged( - BeneficiaryUiState.ShowBeneficiaryTemplate( - response + `when`(beneficiaryRepositoryImp.beneficiaryTemplate()).thenReturn(flowOf(response)) + + viewModel.beneficiaryUiState.test { + viewModel.loadBeneficiaryTemplate() + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals( + BeneficiaryUiState.ShowBeneficiaryTemplate(response), + awaitItem() ) - ) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.SetVisibility(View.VISIBLE)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + assertEquals( + BeneficiaryUiState.SetVisibility(View.VISIBLE), + awaitItem() + ) + verifyNoMoreInteractions(beneficiaryUiStateObserver) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testLoadBeneficiaryTemplate_Unsuccessful() = runBlocking { - `when`(beneficiaryRepositoryImp.beneficiaryTemplate()).thenReturn( - Response.error( - 404, - ResponseBody.create(null, "error") - ) - ) + private fun TestScope.obserrveUiState(): MutableList { + val uiStates = mutableListOf() + viewModel.beneficiaryUiState.onEach { + println(it) + uiStates.add(it) + } + .launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler))) + return uiStates + } - viewModel.loadBeneficiaryTemplate() - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.ShowError(R.string.error_fetching_beneficiary_template)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + @Test(expected = Exception::class) + fun testLoadBeneficiaryTemplate_Unsuccessful() = runTest { + val exception = Exception("Test exception") + `when`(beneficiaryRepositoryImp.beneficiaryTemplate()).thenThrow(exception as Throwable) + + viewModel.beneficiaryUiState.test { + viewModel.loadBeneficiaryTemplate() + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals( + BeneficiaryUiState.ShowError(R.string.error_fetching_beneficiary_template), + awaitItem() + ) + cancelAndIgnoreRemainingEvents() + } } @Test - fun testCreateBeneficiary_Successful() = runBlocking { - val response = mock(ResponseBody::class.java) + fun testCreateBeneficiary_Successful() = runTest { val beneficiaryPayload = mock(BeneficiaryPayload::class.java) `when`(beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload)).thenReturn( - Response.success( - response + flowOf( + ResponseBody.create(null, "success") ) ) - viewModel.createBeneficiary(beneficiaryPayload) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.CreatedSuccessfully) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + viewModel.beneficiaryUiState.test { + viewModel.createBeneficiary(beneficiaryPayload) + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals(BeneficiaryUiState.CreatedSuccessfully, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testCreateBeneficiary_Unsuccessful() = runBlocking { - val error = RuntimeException("Error Response") + @Test(expected = Exception::class) + fun testCreateBeneficiary_Unsuccessful() = runTest { val beneficiaryPayload = mock(BeneficiaryPayload::class.java) - `when`(beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload)).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) + `when`(beneficiaryRepositoryImp.createBeneficiary(beneficiaryPayload)) + .thenThrow( Exception("Error Response")) + + viewModel.beneficiaryUiState.test { + viewModel.createBeneficiary(beneficiaryPayload) + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals( + BeneficiaryUiState.ShowError(R.string.error_creating_beneficiary), + awaitItem() + ) + cancelAndIgnoreRemainingEvents() + } + } - viewModel.createBeneficiary(beneficiaryPayload) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.ShowError(R.string.error_creating_beneficiary)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) - } @Test - fun testUpdateBeneficiary_Successful() = runBlocking { + fun testUpdateBeneficiary_Successful() = runTest { val response = mock(ResponseBody::class.java) val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java) `when`( @@ -125,36 +170,45 @@ class BeneficiaryApplicationViewModelTest { beneficiaryUpdatePayload ) ).thenReturn( - Response.success(response) + flowOf(response) ) + + viewModel.beneficiaryUiState.test { + + viewModel.updateBeneficiary(123L, beneficiaryUpdatePayload) - viewModel.updateBeneficiary(123L, beneficiaryUpdatePayload) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.UpdatedSuccessfully) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals(BeneficiaryUiState.UpdatedSuccessfully, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testUpdateBeneficiary_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testUpdateBeneficiary_Unsuccessful() = runTest { val beneficiaryUpdatePayload = mock(BeneficiaryUpdatePayload::class.java) `when`( beneficiaryRepositoryImp.updateBeneficiary( 123L, beneficiaryUpdatePayload ) - ).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - - viewModel.updateBeneficiary(123L, beneficiaryUpdatePayload) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.ShowError(R.string.error_updating_beneficiary)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + ).thenThrow( Exception("Error updating beneficiary") as Throwable) + + viewModel.beneficiaryUiState.test { + viewModel.updateBeneficiary(123L, beneficiaryUpdatePayload) + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals( + BeneficiaryUiState.ShowError(R.string.error_updating_beneficiary), + awaitItem() + ) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - viewModel.beneficiaryUiState.removeObserver(beneficiaryUiStateObserver) +// viewModel.beneficiaryUiState.removeObserver(beneficiaryUiStateObserver) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt index 092d2d153..7ba7b3278 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryDetailViewModelTest.kt @@ -2,10 +2,20 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -35,56 +45,44 @@ class BeneficiaryDetailViewModelTest { @Mock lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp - @Mock - lateinit var beneficiaryUiStateObserver: Observer - private lateinit var viewModel: BeneficiaryDetailViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = BeneficiaryDetailViewModel(beneficiaryRepositoryImp) - viewModel.beneficiaryUiState.observeForever(beneficiaryUiStateObserver) } - @Test - fun testDeleteBeneficiary_Successful() = runBlocking { + fun testDeleteBeneficiary_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(ResponseBody::class.java) - - `when`(beneficiaryRepositoryImp.deleteBeneficiary(123L)).thenReturn( - Response.success( - response - ) - ) - - viewModel.deleteBeneficiary(123L) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.DeletedSuccessfully) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + `when`(beneficiaryRepositoryImp.deleteBeneficiary(123L)) + .thenReturn(flowOf(response)) + viewModel.beneficiaryUiState.test { + viewModel.deleteBeneficiary(123L) + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.DeletedSuccessfully, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } - - @Test - fun testDeleteBeneficiary_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testDeleteBeneficiary_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - - `when`(beneficiaryRepositoryImp.deleteBeneficiary(123L)).thenReturn( - Response.error( - 404, - ResponseBody.create(null, "error") + `when`(beneficiaryRepositoryImp.deleteBeneficiary(123L)) + .thenThrow(Exception("Error deleting beneficiary")) + viewModel.beneficiaryUiState.test { + viewModel.deleteBeneficiary(123L) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals( + BeneficiaryUiState.ShowError(R.string.error_deleting_beneficiary), + awaitItem() ) - ) - - viewModel.deleteBeneficiary(123L) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.ShowError(R.string.error_deleting_beneficiary)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } - @After fun tearDown() { - viewModel.beneficiaryUiState.removeObserver(beneficiaryUiStateObserver) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt index a398fd525..1238e3a23 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/BeneficiaryListViewModelTest.kt @@ -3,8 +3,18 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -21,7 +31,9 @@ import org.mockito.Mockito.* import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response +import kotlin.time.ExperimentalTime +@OptIn(ExperimentalTime::class) @RunWith(MockitoJUnitRunner::class) @ExperimentalCoroutinesApi class BeneficiaryListViewModelTest { @@ -39,8 +51,6 @@ class BeneficiaryListViewModelTest { @Mock lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp - @Mock - lateinit var beneficiaryUiStateObserver: Observer private lateinit var viewModel: BeneficiaryListViewModel @@ -48,42 +58,39 @@ class BeneficiaryListViewModelTest { fun setUp() { MockitoAnnotations.openMocks(this) viewModel = BeneficiaryListViewModel(beneficiaryRepositoryImp) - viewModel.beneficiaryUiState.observeForever(beneficiaryUiStateObserver) } + + @Test - fun testLoadBeneficiaries_Successful() = runBlocking { + fun testLoadBeneficiaries_Successful() = runTest { val list1 = mock(Beneficiary::class.java) val list2 = mock(Beneficiary::class.java) val list = listOf(list1, list2) - `when`(beneficiaryRepositoryImp.beneficiaryList()).thenReturn(Response.success(list)) - - viewModel.loadBeneficiaries() - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.ShowBeneficiaryList(list)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + `when`(beneficiaryRepositoryImp.beneficiaryList()).thenReturn(flowOf(list)) + viewModel.beneficiaryUiState.test { + viewModel.loadBeneficiaries() + assertEquals(BeneficiaryUiState.Initial, awaitItem()) + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals(BeneficiaryUiState.ShowBeneficiaryList(list), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testLoadBeneficiaries_Unsuccessful() = runBlocking { - val error = RuntimeException("Error Response") - - `when`(beneficiaryRepositoryImp.beneficiaryList()).thenReturn( - Response.error( - 404, - ResponseBody.create(null, "error") - ) - ) - - viewModel.loadBeneficiaries() - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.Loading) - verify(beneficiaryUiStateObserver).onChanged(BeneficiaryUiState.ShowError(R.string.beneficiaries)) - verifyNoMoreInteractions(beneficiaryUiStateObserver) + @Test(expected = Exception::class) + fun testLoadBeneficiaries_Unsuccessful() = runTest { + `when`(beneficiaryRepositoryImp.beneficiaryList()).thenThrow(Exception("Error occurred")) + viewModel.beneficiaryUiState.test { + viewModel.loadBeneficiaries() + assertEquals(BeneficiaryUiState.Loading, awaitItem()) + assertEquals(BeneficiaryUiState.ShowError(R.string.beneficiaries), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - viewModel.beneficiaryUiState.removeObserver(beneficiaryUiStateObserver) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt index 9d97ee567..b0d401999 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt @@ -3,8 +3,17 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -16,6 +25,7 @@ import org.mifos.mobile.models.Charge import org.mifos.mobile.models.Page import org.mifos.mobile.repositories.ClientChargeRepositoryImp import org.mifos.mobile.util.RxSchedulersOverrideRule +import org.mifos.mobile.utils.BeneficiaryUiState import org.mifos.mobile.utils.ClientChargeUiState import org.mockito.Mock import org.mockito.Mockito.* @@ -48,163 +58,177 @@ class ClientChargeViewModelTest { @Before fun setUp() { MockitoAnnotations.openMocks(this) - viewModel = ClientChargeViewModel(clientChargeRepositoryImp) - viewModel.clientChargeUiState.observeForever(clientChargeUiStateObserver) + viewModel = ClientChargeViewModel(clientChargeRepositoryImp) } @Test - fun testLoadClientCharges_Successful() = runBlocking { + fun testLoadClientCharges_Successful() = runTest { val charge1 = mock(Charge::class.java) val charge2 = mock(Charge::class.java) val mockChargePage = Page(1, listOf(charge1, charge2)) `when`(clientChargeRepositoryImp.getClientCharges(123L)).thenReturn( - Response.success(mockChargePage) + flowOf(mockChargePage) ) - - viewModel.loadClientCharges(123L) - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged( - ClientChargeUiState.ShowClientCharges( - mockChargePage.pageItems + + viewModel.clientChargeUiState.test { + viewModel.loadClientCharges(123L) + assertEquals(ClientChargeUiState.Initial, awaitItem()) + assertEquals(ClientChargeUiState.Loading,awaitItem()) + assertEquals( + ClientChargeUiState.ShowClientCharges( + mockChargePage.pageItems + ), awaitItem() ) - ) - verifyNoMoreInteractions(clientChargeUiStateObserver) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testLoadClientCharges_Unsuccessful() = runBlocking { + @Test(expected = Throwable::class) + fun testLoadClientCharges_Unsuccessful() = runTest { val errorMessageResId = R.string.client_charges val throwable = Throwable("Error occurred") - `when`(clientChargeRepositoryImp.getClientCharges(123L)).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - - viewModel.loadClientCharges(123L) - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged( - ClientChargeUiState.ShowError( - errorMessageResId + `when`(clientChargeRepositoryImp.getClientCharges(123L)) + .thenThrow(throwable) + + viewModel.clientChargeUiState.test { + viewModel.loadClientCharges(123L) + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowError( + errorMessageResId + ), awaitItem() ) - ) + cancelAndIgnoreRemainingEvents() + } } @Test - fun testLoadLoanAccountCharges_Successful() = runBlocking { + fun testLoadLoanAccountCharges_Successful() = runTest { val charge1 = mock(Charge::class.java) val charge2 = mock(Charge::class.java) val list = listOf(charge1, charge2) - `when`(clientChargeRepositoryImp.getLoanCharges(123L)).thenReturn(Response.success(list)) - - viewModel.loadLoanAccountCharges(123L) - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.ShowClientCharges(list)) - verifyNoMoreInteractions(clientChargeUiStateObserver) + `when`(clientChargeRepositoryImp.getLoanCharges(123L)) + .thenReturn(flowOf(list)) + + viewModel.clientChargeUiState.test { + viewModel.loadLoanAccountCharges(123L) + assertEquals(ClientChargeUiState.Initial, awaitItem()) + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowClientCharges( + list + ), awaitItem() + ) + cancelAndIgnoreRemainingEvents() + } } - - @Test - fun testLoadLoanAccountCharges_Unsuccessful() = runBlocking { + @Test(expected = Throwable::class) + fun testLoadLoanAccountCharges_Unsuccessful() = runTest { val errorMessageResId = R.string.client_charges val throwable = Throwable("Error occurred") - - `when`(clientChargeRepositoryImp.getLoanCharges(123L)).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - - viewModel.loadLoanAccountCharges(123L) - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged( - ClientChargeUiState.ShowError( - errorMessageResId + + `when`(clientChargeRepositoryImp.getLoanCharges(123L)) + .thenThrow(throwable) + viewModel.clientChargeUiState.test { + viewModel.loadLoanAccountCharges(123L) + + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowError( + errorMessageResId + ), awaitItem() ) - ) + cancelAndIgnoreRemainingEvents() + } } @Test - fun testLoadSavingsAccountCharges_Successful() = runBlocking { + fun testLoadSavingsAccountCharges_Successful() = runTest { val charge1 = mock(Charge::class.java) val charge2 = mock(Charge::class.java) val list = listOf(charge1, charge2) - `when`(clientChargeRepositoryImp.getSavingsCharges(123L)).thenReturn(Response.success(list)) - - viewModel.loadSavingsAccountCharges(123L) - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.ShowClientCharges(list)) - verifyNoMoreInteractions(clientChargeUiStateObserver) + `when`(clientChargeRepositoryImp.getSavingsCharges(123L)) + .thenReturn(flowOf(list)) + + viewModel.clientChargeUiState.test { + viewModel.loadSavingsAccountCharges(123L) + assertEquals(ClientChargeUiState.Initial, awaitItem()) + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowClientCharges( + list + ), awaitItem() + ) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testLoadSavingsAccountCharges_Unsuccessful() = runBlocking { + @Test(expected = Throwable::class) + fun testLoadSavingsAccountCharges_Unsuccessful() = runTest { val errorMessageResId = R.string.client_charges val throwable = Throwable("Error occurred") - `when`(clientChargeRepositoryImp.getSavingsCharges(123L)).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - - viewModel.loadSavingsAccountCharges(123L) - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged( - ClientChargeUiState.ShowError( - errorMessageResId + `when`(clientChargeRepositoryImp.getSavingsCharges(123L)) + .thenThrow(throwable) + + viewModel.clientChargeUiState.test { + viewModel.loadSavingsAccountCharges(123L) + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowError( + errorMessageResId + ), awaitItem() ) - ) + cancelAndIgnoreRemainingEvents() + } } @Test - fun loadClientLocalCharges_Successful() = runBlocking { + fun loadClientLocalCharges_Successful() = runTest { val charge1 = mock(Charge::class.java) val charge2 = mock(Charge::class.java) val mockChargePage = Page(1, listOf(charge1, charge2)) `when`(clientChargeRepositoryImp.clientLocalCharges()).thenReturn( - Response.success( - mockChargePage - ) + flowOf(mockChargePage) ) - - viewModel.loadClientLocalCharges() - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged( - ClientChargeUiState.ShowClientCharges( - mockChargePage.pageItems + + viewModel.clientChargeUiState.test { + viewModel.loadClientLocalCharges() + assertEquals(ClientChargeUiState.Initial, awaitItem()) + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowClientCharges( + mockChargePage.pageItems + ), awaitItem() ) - ) - verifyNoMoreInteractions(clientChargeUiStateObserver) + cancelAndIgnoreRemainingEvents() + } } - - @Test - fun loadClientLocalCharges_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun loadClientLocalCharges_Unsuccessful() = runTest { val errorMessageResId = R.string.client_charges val throwable = Throwable("Error occurred") - - `when`(clientChargeRepositoryImp.clientLocalCharges()).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - - viewModel.loadClientLocalCharges() - - verify(clientChargeUiStateObserver).onChanged(ClientChargeUiState.Loading) - verify(clientChargeUiStateObserver).onChanged( - ClientChargeUiState.ShowError( - errorMessageResId + `when`(clientChargeRepositoryImp.clientLocalCharges()).thenThrow(throwable) + + viewModel.clientChargeUiState.test { + viewModel.loadClientLocalCharges() + assertEquals(ClientChargeUiState.Loading, awaitItem()) + assertEquals( + ClientChargeUiState.ShowError( + errorMessageResId + ), awaitItem() ) - ) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - viewModel.clientChargeUiState.removeObserver(clientChargeUiStateObserver) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt index 062552223..3a20062ae 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/GuarantorDetailViewModelTest.kt @@ -3,9 +3,11 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.Before import org.junit.Rule @@ -37,8 +39,6 @@ class GuarantorDetailViewModelTest { @Mock private lateinit var guarantorRepositoryImp: GuarantorRepositoryImp - @Mock - lateinit var guarantorUiStateObserver: Observer lateinit var viewModel: GuarantorDetailViewModel @@ -49,28 +49,30 @@ class GuarantorDetailViewModelTest { } @Test - fun testDeleteGuarantor_Successful() = runBlocking { + fun testDeleteGuarantor_Successful() = runTest { val response = mock(ResponseBody::class.java) `when`(guarantorRepositoryImp.deleteGuarantor(1L, 2L)).thenReturn(flowOf(response)) - - viewModel.deleteGuarantor(1L, 2L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged( - GuarantorUiState.GuarantorDeletedSuccessfully( - response.string() - ) - ) + viewModel.guarantorUiState.test { + viewModel.deleteGuarantor(1L, 2L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals(GuarantorUiState.GuarantorDeletedSuccessfully(response.string()), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testDeleteGuarantor_Unsuccessful() = runBlocking { - val error = IOException("Error") - `when`(guarantorRepositoryImp.deleteGuarantor(1L, 2L)).thenThrow(error) + @Test(expected = Exception::class) + fun testDeleteGuarantor_Unsuccessful() = runTest { + val error = Exception("Error") + `when`(guarantorRepositoryImp.deleteGuarantor(1L, 2L)) + .thenThrow(error) + viewModel.guarantorUiState.test { + viewModel.deleteGuarantor(1L, 2L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals(GuarantorUiState.ShowError(error.message), awaitItem()) + cancelAndIgnoreRemainingEvents() + } - viewModel.deleteGuarantor(1L, 2L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.ShowError(Throwable().message)) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt index 89ddf4977..a01cc15c4 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/GuarantorListViewModelTest.kt @@ -3,9 +3,12 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -37,9 +40,6 @@ class GuarantorListViewModelTest { @Mock private lateinit var guarantorRepositoryImp: GuarantorRepositoryImp - @Mock - lateinit var guarantorUiStateObserver: Observer - private lateinit var viewModel: GuarantorListViewModel @Before @@ -49,29 +49,29 @@ class GuarantorListViewModelTest { } @Test - fun testGetGuarantorList_Successful() = runBlocking { + fun testGetGuarantorList_Successful() = runTest { val list1 = mock(GuarantorPayload::class.java) val list2 = mock(GuarantorPayload::class.java) val list = listOf(list1, list2) `when`(guarantorRepositoryImp.getGuarantorList(1L)).thenReturn(flowOf(list)) - - viewModel.getGuarantorList(1L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged( - GuarantorUiState.ShowGuarantorListSuccessfully( - list - ) - ) + viewModel.guarantorUiState.test { + viewModel.getGuarantorList(1L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals(GuarantorUiState.ShowGuarantorListSuccessfully(list), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testGetGuarantorList_Unsuccessful() = runBlocking { - val error = IOException("Error") + @Test(expected = Exception::class) + fun testGetGuarantorList_Unsuccessful() = runTest { + val error = Exception("Error") `when`(guarantorRepositoryImp.getGuarantorList(1L)).thenThrow(error) - - viewModel.getGuarantorList(1L) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.Loading) - verify(guarantorUiStateObserver).onChanged(GuarantorUiState.ShowError(Throwable().message)) + viewModel.guarantorUiState.test { + viewModel.getGuarantorList(1L) + assertEquals(GuarantorUiState.Loading, awaitItem()) + assertEquals(GuarantorUiState.ShowError(error.message), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt index c9e2385c8..1ff142ada 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt @@ -2,7 +2,10 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test import junit.framework.Assert.assertEquals +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -23,54 +26,38 @@ class HelpViewModelTest { @Mock private lateinit var mockFAQArrayList: ArrayList - @Mock - private lateinit var helpUiStateObserver: Observer - private lateinit var viewModel: HelpViewModel @Before fun setUp() { viewModel = HelpViewModel() - viewModel.helpUiState.observeForever(helpUiStateObserver) } @Test - fun testLoadFaq() { + fun testLoadFaq() = runTest { val qs = arrayOf("Question1", "Question2") val ans = arrayOf("Answer1", "Answer2") viewModel.loadFaq(qs, ans) - verify(helpUiStateObserver).onChanged( - HelpUiState.ShowFaq( - arrayListOf( - FAQ( - "Question1", - "Answer1", - false - ), - FAQ( - "Question2", - "Answer2", - false - ) - ) - ) - ) - verifyNoMoreInteractions(helpUiStateObserver) + viewModel.helpUiState.test{ + assertEquals(HelpUiState.ShowFaq(arrayListOf(FAQ("Question1", "Answer1", false), FAQ("Question2", "Answer2", false))) + , awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @Test - fun testFilterList() { + fun testFilterList() = runTest { val query = "app" - val mockFAQ1 = mock(FAQ::class.java) - val mockFAQ2 = mock(FAQ::class.java) - - `when`(mockFAQ1.question).thenReturn("How to use the app?") - `when`(mockFAQ2.question).thenReturn("Is there a user guide available?") - - val filteredList = viewModel.filterList(arrayListOf(mockFAQ1,mockFAQ2), query) - - assertEquals(mockFAQ1, filteredList[0]) + val mockFAQ1 =FAQ("How to use the app?", "Answer1", false) + val mockFAQ2 = FAQ("Is there a user guide available?", "Answer2", false) + viewModel.loadFaq(arrayOf("How to use the app?", "Is there a user guide available?"), arrayOf("Answer1", "Answer2")) + advanceUntilIdle() + val filteredList = viewModel.filterList(query) + viewModel.helpUiState.test{ + assertEquals(HelpUiState.ShowFaq(arrayListOf(mockFAQ1)), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } } diff --git a/app/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt index b2978e6f3..5fd48ac0e 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/HomeViewModelTest.kt @@ -2,11 +2,13 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import io.reactivex.Observer import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -18,9 +20,10 @@ import org.mifos.mobile.models.accounts.savings.SavingAccount import org.mifos.mobile.models.client.Client import org.mifos.mobile.models.client.ClientAccounts import org.mifos.mobile.repositories.HomeRepositoryImp +import org.mifos.mobile.ui.home.HomeState +import org.mifos.mobile.ui.home.HomeUiState import org.mifos.mobile.ui.home.HomeViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule -import org.mifos.mobile.utils.HomeUiState import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.mock @@ -46,6 +49,7 @@ class HomeViewModelTest { @Mock private lateinit var mockPreferencesHelper: PreferencesHelper + private lateinit var viewModel: HomeViewModel @@ -57,91 +61,88 @@ class HomeViewModelTest { } @Test - fun testLoadingClientAccountDetails_Success(): Unit = runBlocking { - val mockLoanAccounts = listOf(mock(LoanAccount::class.java)) - val mockSavingsAccounts = listOf(mock(SavingAccount::class.java)) + fun testLoadingClientAccountDetails_Success(): Unit = runTest{ + val mockLoanAccounts = listOf( + mock(LoanAccount::class.java).apply { + `when`(loanBalance).thenReturn(100.0) + } + ) + val mockSavingsAccounts = listOf( + mock(SavingAccount::class.java).apply { + `when`(accountBalance).thenReturn(100.0) + } + ) val expectedLoanBalance = 100.0 val expectedSavingBalance = 100.0 + val mockClient = ClientAccounts(mockLoanAccounts, mockSavingsAccounts) - `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mock(ClientAccounts::class.java))) + `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mockClient)) viewModel.loadClientAccountDetails() - viewModel.homeUiState.collect { value -> - assertEquals( - HomeUiState.ClientAccountDetails( - expectedLoanBalance, - expectedSavingBalance - ), value + assertEquals(viewModel.homeUiState.value, + HomeUiState.Success( + HomeState( loanAmount = expectedLoanBalance, + savingsAmount = expectedSavingBalance)) ) - } + } - @Test - fun testLoadingClientAccountDetails_Error(): Unit = runBlocking { + @Test(expected = Exception::class) + fun testLoadingClientAccountDetails_Error(): Unit = runTest{ val errorMessageResId = R.string.error_fetching_accounts - `when`(homeRepositoryImp.clientAccounts()).thenThrow(RuntimeException()) + `when`(homeRepositoryImp.clientAccounts()).thenThrow( Exception()) viewModel.loadClientAccountDetails() - viewModel.homeUiState.collect { value -> - assertTrue(value is HomeUiState.Error) - assertEquals(errorMessageResId, (value as HomeUiState.Error)) - } + assertTrue(viewModel.homeUiState.value is HomeUiState.Error) + assertEquals(errorMessageResId, (viewModel.homeUiState.value as HomeUiState.Error).errorMessage) } @Test - fun testLoadingUserDetails_Success(): Unit = runBlocking { + fun testLoadingUserDetails_Success(): Unit = runTest{ val mockClient = mock(Client::class.java) `when`(homeRepositoryImp.currentClient()).thenReturn(flowOf(mockClient)) - viewModel.userDetails - - viewModel.homeUiState.collect { value -> - assertTrue(value is HomeUiState.UserDetails) - assertEquals(mockClient, (value as HomeUiState.UserDetails).client) - } + viewModel.getUserDetails() + assertTrue(viewModel.homeUiState.value is HomeUiState.Success) + assertEquals(mockClient.officeName, + (viewModel.homeUiState.value as HomeUiState.Success).homeState.username) } - @Test - fun testLoadingUserDetails_Error(): Unit = runBlocking { + @Test(expected = Exception::class) + fun testLoadingUserDetails_Error(): Unit = runTest{ val errorMessageResId = R.string.error_fetching_client - `when`(homeRepositoryImp.currentClient()).thenThrow(RuntimeException()) + `when`(homeRepositoryImp.currentClient()).thenThrow( Exception()) - viewModel.userDetails + viewModel.getUserDetails() + assertTrue(viewModel.homeUiState.value is HomeUiState.Error) + assertEquals(errorMessageResId,HomeUiState.Error(R.string.error_fetching_client)) - viewModel.homeUiState.collect { value -> - assertTrue(value is HomeUiState.Error) - assertEquals(errorMessageResId, (value as HomeUiState.Error)) - } } @Test - fun testLoadingUnreadNotificationsCount_Success(): Unit = runBlocking { + fun testLoadingUnreadNotificationsCount_Success(): Unit = runTest{ val mockUnreadCount = 5 `when`(homeRepositoryImp.unreadNotificationsCount()).thenReturn(flowOf(mockUnreadCount)) viewModel.unreadNotificationsCount - viewModel.homeUiState.collect { value -> - assertTrue(value is HomeUiState.UnreadNotificationsCount) - assertEquals(mockUnreadCount, (value as HomeUiState.UnreadNotificationsCount).count) - } + assertEquals(mockUnreadCount,viewModel.notificationsCount.value) + } - @Test - fun testLoadingUnreadNotificationsCount_Error(): Unit = runBlocking { - `when`(homeRepositoryImp.unreadNotificationsCount()).thenThrow(RuntimeException()) + @Test(expected = Exception::class) + fun testLoadingUnreadNotificationsCount_Error(): Unit = runTest{ + `when`(homeRepositoryImp.unreadNotificationsCount()). + thenThrow( Exception("Error message ")) viewModel.unreadNotificationsCount - viewModel.homeUiState.collect { value -> - assertTrue(value is HomeUiState.UnreadNotificationsCount) - assertEquals(0, (value as HomeUiState.UnreadNotificationsCount).count) - } + assertEquals(0, viewModel.notificationsCount.value) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt index 1a2a19c94..b680860ed 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountTransactionViewModelTest.kt @@ -2,9 +2,12 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.After @@ -13,6 +16,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mifos.mobile.R +import org.mifos.mobile.models.Transaction import org.mifos.mobile.models.accounts.loan.LoanWithAssociations import org.mifos.mobile.repositories.LoanRepositoryImp import org.mifos.mobile.util.RxSchedulersOverrideRule @@ -37,8 +41,6 @@ class LoanAccountTransactionViewModelTest { @Mock lateinit var loanRepositoryImp: LoanRepositoryImp - @Mock - lateinit var loanUiStateObserver: Observer private lateinit var viewModel: LoanAccountTransactionViewModel @@ -46,11 +48,10 @@ class LoanAccountTransactionViewModelTest { fun setUp() { MockitoAnnotations.openMocks(this) viewModel = LoanAccountTransactionViewModel(loanRepositoryImp) - viewModel.loanUiState.observeForever(loanUiStateObserver) } @Test - fun testLoadLoanAccountDetails_Successful_WithEmptyTransaction() = runBlocking { + fun testLoadLoanAccountDetails_Successful_WithEmptyTransaction() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(LoanWithAssociations::class.java) `when`( @@ -58,56 +59,58 @@ class LoanAccountTransactionViewModelTest { Constants.TRANSACTIONS, 1 ) - ).thenReturn(Response.success(response)) - - viewModel.loadLoanAccountDetails(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowEmpty(response)) - verifyNoMoreInteractions(loanUiStateObserver) + ).thenReturn(flowOf(response)) + viewModel.loanUiState.test { + viewModel.loadLoanAccountDetails(1) + assertEquals(LoanUiState.Loading,awaitItem()) + assertEquals(LoanUiState.ShowEmpty(response),awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @Test - fun testLoadLoanAccountDetails_Successful_WithNonEmptyTransaction() = runBlocking { + fun testLoadLoanAccountDetails_Successful_WithNonEmptyTransaction() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val response = mock(LoanWithAssociations::class.java) + val response = mock(LoanWithAssociations::class.java).apply { + `when`(transactions).thenReturn(mutableListOf(mock(Transaction::class.java))) + } `when`( loanRepositoryImp.getLoanWithAssociations( Constants.TRANSACTIONS, 1 ) - ).thenReturn(Response.success(response)) - - viewModel.loadLoanAccountDetails(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - if (response.transactions != null && response?.transactions?.isNotEmpty() == true) { - verify(loanUiStateObserver).onChanged(LoanUiState.ShowLoan(response)) - verifyNoMoreInteractions(loanUiStateObserver) + ).thenReturn(flowOf(response)) + viewModel.loanUiState.test { + viewModel.loadLoanAccountDetails(1) + assertEquals(LoanUiState.Loading,awaitItem()) + assertEquals(LoanUiState.ShowLoan(response),awaitItem()) + cancelAndIgnoreRemainingEvents() } Dispatchers.resetMain() } - @Test - fun testLoadLoanAccountDetails_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testLoadLoanAccountDetails_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) `when`( loanRepositoryImp.getLoanWithAssociations( Constants.TRANSACTIONS, 1 ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - viewModel.loadLoanAccountDetails(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowError(R.string.loan_account_details)) - verifyNoMoreInteractions(loanUiStateObserver) + ).thenThrow(Exception("Error occurred")) + viewModel.loanUiState.test { + viewModel.loadLoanAccountDetails(1) + assertEquals(LoanUiState.Loading,awaitItem()) + assertEquals(LoanUiState.ShowError(R.string.loan_account_details),awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @After fun tearDown() { - viewModel.loanUiState.removeObserver(loanUiStateObserver) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt index 7baf7b0cf..a4be6027a 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountWithdrawViewModelTest.kt @@ -2,9 +2,12 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.After @@ -36,20 +39,17 @@ class LoanAccountWithdrawViewModelTest { @Mock lateinit var loanRepositoryImp: LoanRepositoryImp - @Mock - lateinit var loanUiStateObserver: Observer - private lateinit var viewModel: LoanAccountWithdrawViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = LoanAccountWithdrawViewModel(loanRepositoryImp) - viewModel.loanUiState.observeForever(loanUiStateObserver) + } @Test - fun testWithdrawLoanAccount_Successful() = runBlocking { + fun testWithdrawLoanAccount_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(ResponseBody::class.java) val mockLoanWithdraw = mock(LoanWithdraw::class.java) @@ -58,16 +58,18 @@ class LoanAccountWithdrawViewModelTest { 1, mockLoanWithdraw ) - ).thenReturn(Response.success(response)) - viewModel.withdrawLoanAccount(1, mockLoanWithdraw) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.WithdrawSuccess) - verifyNoMoreInteractions(loanUiStateObserver) + ).thenReturn(flowOf(response)) + viewModel.loanUiState.test { + viewModel.withdrawLoanAccount(1, mockLoanWithdraw) + assertEquals( LoanUiState.Loading,awaitItem()) + assertEquals( LoanUiState.WithdrawSuccess,awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } - @Test - fun testWithdrawLoanAccount_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testWithdrawLoanAccount_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val mockLoanWithdraw = mock(LoanWithdraw::class.java) `when`( @@ -75,17 +77,19 @@ class LoanAccountWithdrawViewModelTest { 1, mockLoanWithdraw ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - viewModel.withdrawLoanAccount(1, mockLoanWithdraw) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowError(R.string.error_loan_account_withdraw)) - verifyNoMoreInteractions(loanUiStateObserver) + ).thenThrow(Exception("Error occurred")) + viewModel.loanUiState.test { + viewModel.withdrawLoanAccount(1, mockLoanWithdraw) + assertEquals( LoanUiState.Loading,awaitItem()) + assertEquals( LoanUiState.ShowError(R.string.error_loan_account_withdraw),awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @After fun tearDown() { - viewModel.loanUiState.removeObserver(loanUiStateObserver) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt index 9303101d5..0d4bf62bb 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoanAccountsDetailViewModelTest.kt @@ -2,9 +2,13 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.After @@ -15,6 +19,7 @@ import org.junit.runner.RunWith import org.mifos.mobile.R import org.mifos.mobile.models.accounts.loan.LoanWithAssociations import org.mifos.mobile.repositories.LoanRepositoryImp +import org.mifos.mobile.ui.loan_account.LoanAccountDetailUiState import org.mifos.mobile.ui.loan_account.LoanAccountsDetailViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.utils.Constants @@ -39,20 +44,17 @@ class LoanAccountsDetailViewModelTest { @Mock lateinit var loanRepositoryImp: LoanRepositoryImp - @Mock - lateinit var loanUiStateObserver: Observer - lateinit var viewModel: LoanAccountsDetailViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = LoanAccountsDetailViewModel(loanRepositoryImp) - viewModel.loanUiState.observeForever(loanUiStateObserver) + } @Test - fun testLoadLoanAccountDetails_Successful() = runBlocking { + fun testLoadLoanAccountDetails_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(LoanWithAssociations::class.java) @@ -61,34 +63,32 @@ class LoanAccountsDetailViewModelTest { Constants.REPAYMENT_SCHEDULE, 1 ) - ).thenReturn(Response.success(response)) - + ).thenReturn(flowOf(response)) viewModel.loadLoanAccountDetails(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowLoan(response)) - verifyNoMoreInteractions(loanUiStateObserver) + advanceUntilIdle() + println(viewModel.loanUiState.value) + assertEquals(viewModel.loanUiState.value, LoanAccountDetailUiState.Success(response)) Dispatchers.resetMain() } - @Test - fun testLoadLoanAccountDetails_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testLoadLoanAccountDetails_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) `when`( loanRepositoryImp.getLoanWithAssociations( Constants.REPAYMENT_SCHEDULE, 1 ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - + ).thenThrow(Exception("Error occurred")) viewModel.loadLoanAccountDetails(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowError(R.string.loan_account_details)) - verifyNoMoreInteractions(loanUiStateObserver) + advanceUntilIdle() + assertEquals(viewModel.loanUiState.value, + LoanAccountDetailUiState.Error) Dispatchers.resetMain() } @After fun tearDown() { - viewModel.loanUiState.removeObserver(loanUiStateObserver) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt index 3075b4ebc..2d0793586 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoanApplicationViewModelTest.kt @@ -2,9 +2,13 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.After @@ -37,102 +41,86 @@ class LoanApplicationViewModelTest { @Mock lateinit var loanRepositoryImp: LoanRepositoryImp - @Mock - lateinit var loanUiStateObserver: Observer - private lateinit var viewModel: LoanApplicationViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = LoanApplicationViewModel(loanRepositoryImp) - viewModel.loanUiState.observeForever(loanUiStateObserver) + } @Test - fun testLoadLoanApplicationTemplate_Successful() = runBlocking { + fun testLoadLoanApplicationTemplate_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(LoanTemplate::class.java) val mockLoanState = mock(LoanState::class.java) - `when`(loanRepositoryImp.template()).thenReturn(Response.success(response)) - viewModel.loadLoanApplicationTemplate(mockLoanState) - - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - if (mockLoanState == LoanState.CREATE) { - verify(loanUiStateObserver).onChanged(LoanUiState.ShowLoanTemplateByProduct(response)) - verifyNoMoreInteractions(loanUiStateObserver) - } else { - verify(loanUiStateObserver).onChanged( - LoanUiState.ShowUpdateLoanTemplateByProduct( - response - ) - ) - verifyNoMoreInteractions(loanUiStateObserver) + `when`(loanRepositoryImp.template()).thenReturn(flowOf(response)) + viewModel.loanUiState.test { + viewModel.loadLoanApplicationTemplate(mockLoanState) + assertEquals(LoanUiState.Loading, awaitItem()) + if (mockLoanState == LoanState.CREATE) { + assertEquals(LoanUiState.ShowLoanTemplateByProduct(response), awaitItem()) + } else { + assertEquals(LoanUiState.ShowUpdateLoanTemplateByProduct(response), awaitItem()) + } } Dispatchers.resetMain() } - @Test - fun testLoadLoanApplicationTemplate_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testLoadLoanApplicationTemplate_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val loanState = mock(LoanState::class.java) - `when`(loanRepositoryImp.template()).thenReturn( - Response.error( - 404, - ResponseBody.create(null, "error") - ) - ) + `when`(loanRepositoryImp.template()) + .thenThrow(Exception("Error occurred")) + viewModel.loanUiState.test { viewModel.loadLoanApplicationTemplate(loanState) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowError(R.string.error_fetching_template)) - verifyNoMoreInteractions(loanUiStateObserver) + assertEquals(LoanUiState.Loading, awaitItem()) + assertEquals(LoanUiState.ShowError(R.string.error_fetching_template), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @Test - fun loadLoanApplicationTemplateByProduct_Successful() = runBlocking { + fun loadLoanApplicationTemplateByProduct_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(LoanTemplate::class.java) val mockLoanState = mock(LoanState::class.java) - `when`(loanRepositoryImp.getLoanTemplateByProduct(1)).thenReturn(Response.success(response)) - viewModel.loadLoanApplicationTemplateByProduct(1, mockLoanState) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - if (mockLoanState == LoanState.CREATE) { - verify(loanUiStateObserver).onChanged(LoanUiState.ShowLoanTemplate(response)) - verifyNoMoreInteractions(loanUiStateObserver) - } else { - verify(loanUiStateObserver).onChanged( - LoanUiState.ShowUpdateLoanTemplate( - response - ) - ) - verifyNoMoreInteractions(loanUiStateObserver) + `when`(loanRepositoryImp.getLoanTemplateByProduct(1)).thenReturn(flowOf(response)) + viewModel.loanUiState.test { + viewModel.loadLoanApplicationTemplateByProduct(1, mockLoanState) + assertEquals(LoanUiState.Loading, awaitItem()) + if (mockLoanState == LoanState.CREATE) { + assertEquals(LoanUiState.ShowLoanTemplate(response), awaitItem()) + } else { + assertEquals(LoanUiState.ShowUpdateLoanTemplate(response), awaitItem()) + } } Dispatchers.resetMain() } - @Test - fun loadLoanApplicationTemplateByProduct_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun loadLoanApplicationTemplateByProduct_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val mockLoanState = mock(LoanState::class.java) - `when`(loanRepositoryImp.getLoanTemplateByProduct(1)).thenReturn( - Response.error( - 404, - ResponseBody.create(null, "error") - ) - ) - viewModel.loadLoanApplicationTemplateByProduct(1, mockLoanState) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowError(R.string.error_fetching_template)) - verifyNoMoreInteractions(loanUiStateObserver) + `when`(loanRepositoryImp.getLoanTemplateByProduct(1)) + .thenThrow(Exception("Error occurred")) + viewModel.loanUiState.test { + viewModel.loadLoanApplicationTemplateByProduct(1, mockLoanState) + assertEquals(LoanUiState.Loading, awaitItem()) + assertEquals(LoanUiState.ShowError(R.string.error_fetching_template), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @After fun tearDown() { - viewModel.loanUiState.removeObserver(loanUiStateObserver) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt index 56eb2f698..b99329179 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoanRepaymentScheduleViewModelTest.kt @@ -2,9 +2,13 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.After @@ -37,20 +41,17 @@ class LoanRepaymentScheduleViewModelTest { @Mock lateinit var loanRepositoryImp: LoanRepositoryImp - @Mock - lateinit var loanUiStateObserver: Observer - private lateinit var viewModel: LoanRepaymentScheduleViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = LoanRepaymentScheduleViewModel(loanRepositoryImp) - viewModel.loanUiState.observeForever(loanUiStateObserver) + } @Test - fun testLoanLoanWithAssociations_Successful() = runBlocking { + fun testLoanLoanWithAssociations_Successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val response = mock(LoanWithAssociations::class.java) `when`( @@ -58,40 +59,40 @@ class LoanRepaymentScheduleViewModelTest { Constants.REPAYMENT_SCHEDULE, 1 ) - ).thenReturn(Response.success(response)) - - viewModel.loanLoanWithAssociations(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - if (response.repaymentSchedule?.periods?.isNotEmpty() == true) { - verify(loanUiStateObserver).onChanged(LoanUiState.ShowLoan(response)) - verifyNoMoreInteractions(loanUiStateObserver) - } else { - verify(loanUiStateObserver).onChanged(LoanUiState.ShowEmpty(response)) - verifyNoMoreInteractions(loanUiStateObserver) + ).thenReturn(flowOf(response)) + viewModel.loanUiState.test { + viewModel.loanLoanWithAssociations(1) + assertEquals(LoanUiState.Loading, awaitItem()) + if (response.repaymentSchedule?.periods?.isNotEmpty() == true) { + assertEquals(LoanUiState.ShowLoan(response), awaitItem()) + } else { + assertEquals(LoanUiState.ShowEmpty(response), awaitItem()) + } } Dispatchers.resetMain() } - @Test - fun testLoanLoanWithAssociations_Unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun testLoanLoanWithAssociations_Unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) `when`( loanRepositoryImp.getLoanWithAssociations( Constants.REPAYMENT_SCHEDULE, 1 ) - ).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - viewModel.loanLoanWithAssociations(1) - verify(loanUiStateObserver).onChanged(LoanUiState.Loading) - verify(loanUiStateObserver).onChanged(LoanUiState.ShowError(R.string.repayment_schedule)) + ).thenThrow(Exception("Error occurred")) + viewModel.loanUiState.test { + viewModel.loanLoanWithAssociations(1) + assertEquals(LoanUiState.Loading, awaitItem()) + assertEquals(LoanUiState.ShowError(R.string.repayment_schedule), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @After fun tearDown() { - viewModel.loanUiState.removeObserver(loanUiStateObserver) + } diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt index 953c5e8a6..e7699c061 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt @@ -2,9 +2,12 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.* @@ -20,6 +23,7 @@ import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.utils.LoginUiState import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response @@ -39,10 +43,7 @@ class LoginViewModelTest { @Mock lateinit var clientRepositoryImp: ClientRepository - - @Mock - lateinit var loginUiStateObserver: Observer - + private lateinit var mockUser: User private lateinit var loginViewModel: LoginViewModel private var emptyClientPage: Page? = null @@ -52,7 +53,6 @@ class LoginViewModelTest { fun setUp() { MockitoAnnotations.openMocks(this) loginViewModel = LoginViewModel(userAuthRepositoryImp, clientRepositoryImp) - loginViewModel.loginUiState.observeForever(loginUiStateObserver) mockUser = FakeRemoteDataSource.user emptyClientPage = FakeRemoteDataSource.noClients clientPage = FakeRemoteDataSource.clients @@ -107,88 +107,100 @@ class LoginViewModelTest { } @Test - fun testLogin_SuccessfulLoginReceivedFromRepository_ReturnsLoginSuccess() = runBlocking { + fun testLogin_SuccessfulLoginReceivedFromRepository_ReturnsLoginSuccess() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) Mockito.`when`( userAuthRepositoryImp.login(Mockito.anyString(), Mockito.anyString()) - ).thenReturn(Response.success(mockUser)) - - loginViewModel.login("username", "password") - - Mockito.verify(loginUiStateObserver).onChanged(LoginUiState.Loading) - Mockito.verify(clientRepositoryImp).saveAuthenticationTokenForSession(mockUser) - Mockito.verify(loginUiStateObserver).onChanged(LoginUiState.LoginSuccess) - Mockito.verifyNoMoreInteractions(loginUiStateObserver) + ).thenReturn(flowOf(mockUser)) + loginViewModel.loginUiState.test { + loginViewModel.login("username", "password") + Assert.assertEquals(LoginUiState.Initial, awaitItem()) + Assert.assertEquals(LoginUiState.LoginSuccess, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } - @Test - fun testLogin_UnsuccessfulLoginReceivedFromRepository_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testLogin_UnsuccessfulLoginReceivedFromRepository_ReturnsError() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) Mockito.`when`( userAuthRepositoryImp.login(Mockito.anyString(), Mockito.anyString()) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - loginViewModel.login("username", "password") - - Mockito.verify(loginUiStateObserver).onChanged(LoginUiState.Loading) - Mockito.verify(loginUiStateObserver).onChanged(LoginUiState.Error) - Mockito.verifyNoMoreInteractions(loginUiStateObserver) + ).thenThrow(Exception("Error occurred")) + loginViewModel.loginUiState.test { + loginViewModel.login("username", "password") + Assert.assertEquals(LoginUiState.Initial, awaitItem()) + Assert.assertEquals(LoginUiState.Error, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } - @Test - fun testLoadClient_UnsuccessfulLoadClientReceivedFromRepository_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testLoadClient_UnsuccessfulLoadClientReceivedFromRepository_ReturnsError() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) Mockito.`when`( clientRepositoryImp.loadClient() - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - loginViewModel.loadClient() - - Mockito.verify(loginUiStateObserver).onChanged(LoginUiState.Error) + ).thenThrow(Exception("Error occurred")) + loginViewModel.loginUiState.test { + loginViewModel.loadClient() + Assert.assertEquals(LoginUiState.Initial, awaitItem()) + Assert.assertEquals(LoginUiState.Loading, awaitItem()) + Assert.assertEquals(LoginUiState.Error, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Mockito.verify(clientRepositoryImp).clearPrefHelper() Mockito.verify(clientRepositoryImp).reInitializeService() - Mockito.verifyNoMoreInteractions(loginUiStateObserver) Dispatchers.resetMain() } - @Test - fun testLoadClient_EmptyClientPageReceivedFromRepository_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testLoadClient_EmptyClientPageReceivedFromRepository_ReturnsError() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) Mockito.`when`( clientRepositoryImp.loadClient() - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - loginViewModel.loadClient() - - Mockito.verify(loginUiStateObserver).onChanged(LoginUiState.Error) - Mockito.verifyNoMoreInteractions(loginUiStateObserver) + ).thenThrow(Exception("Error occurred")) + loginViewModel.loginUiState.test { + loginViewModel.loadClient() + Assert.assertEquals(LoginUiState.Initial, awaitItem()) + Assert.assertEquals(LoginUiState.Loading, awaitItem()) + Assert.assertEquals(LoginUiState.Error, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @Test fun testLoadClient_NonEmptyClientPageReceivedFromRepository_ReturnsLoadClientSuccess() = - runBlocking { + runTest { Dispatchers.setMain(Dispatchers.Unconfined) val clientId = clientPage?.pageItems?.get(0)?.id?.toLong() val clientName = clientPage?.pageItems?.get(0)?.displayName + val client = mock(Client::class.java).apply { + Mockito.`when`(id).thenReturn(clientId?.toInt()) + Mockito.`when`(displayName).thenReturn(clientName) + } + val clientPage1 = Page().apply { + pageItems = listOf(client) + } Mockito.`when`( clientRepositoryImp.loadClient() - ).thenReturn(Response.success(clientPage)) - - loginViewModel.loadClient() - - Mockito.verify(clientRepositoryImp).setClientId(clientId) - Mockito.verify(clientRepositoryImp).reInitializeService() - Mockito.verify(loginUiStateObserver) - .onChanged(LoginUiState.LoadClientSuccess(clientName)) - Mockito.verifyNoMoreInteractions(loginUiStateObserver) + ).thenReturn( + flow { + emit(clientPage1 as Page) + } + ) + loginViewModel.loginUiState.test { + loginViewModel.loadClient() + Assert.assertEquals(LoginUiState.Initial, awaitItem()) + Assert.assertEquals(LoginUiState.LoadClientSuccess(clientName), awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } @After fun tearDown() { - loginViewModel.loginUiState.removeObserver(loginUiStateObserver) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt index 0966ec6f9..fb968a227 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/NotificationViewModelTest.kt @@ -3,9 +3,10 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Assert.* import org.junit.Before @@ -38,54 +39,45 @@ class NotificationViewModelTest { @Mock lateinit var notificationRepositoryImp: NotificationRepository - @Mock - lateinit var notificationUiStateObserver: Observer - private lateinit var notificationViewModel: NotificationViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) notificationViewModel = NotificationViewModel(notificationRepositoryImp) - notificationViewModel.notificationUiState.observeForever(notificationUiStateObserver) } @Test fun testLoadNotifications_NotificationsSuccessfullyReceivedFromRepository_ReturnsNotificationsSuccessfully() = - runBlocking { + runTest { val dummyNotifications = listOf(MifosNotification(), MifosNotification()) `when`(notificationRepositoryImp.loadNotifications()).thenReturn( flowOf( dummyNotifications ) ) - val observer = mock>() - notificationViewModel.notificationUiState.observeForever(observer) - + notificationViewModel.notificationUiState.test { + notificationViewModel.loadNotifications() + assertEquals(NotificationUiState.Initial, awaitItem()) + assertEquals(NotificationUiState.Loading, awaitItem()) + assertEquals(NotificationUiState.LoadNotificationsSuccessful(dummyNotifications), awaitItem()) + cancelAndIgnoreRemainingEvents() + } +} + + @Test(expected = Exception::class) + fun testLoadNotifications_NotificationsNotReceivedFromRepository_ReturnsError() = runTest { + `when`(notificationRepositoryImp.loadNotifications()).thenThrow(Exception("Dummy error")) + notificationViewModel.notificationUiState.test { notificationViewModel.loadNotifications() - - verify(observer).onChanged( - NotificationUiState.LoadNotificationsSuccessful( - dummyNotifications - ) - ) - verifyNoMoreInteractions(notificationUiStateObserver) + assertEquals(NotificationUiState.Initial, awaitItem()) + assertEquals(NotificationUiState.Loading, awaitItem()) + assertEquals(NotificationUiState.Error, awaitItem()) + cancelAndIgnoreRemainingEvents() } - - @Test - fun testLoadNotifications_NotificationsNotReceivedFromRepository_ReturnsError() = runBlocking { - `when`(notificationRepositoryImp.loadNotifications()).thenThrow(Exception("Dummy error")) - val observer = mock>() - notificationViewModel.notificationUiState.observeForever(observer) - - notificationViewModel.loadNotifications() - - verify(observer).onChanged(NotificationUiState.Error) - verifyNoMoreInteractions(notificationUiStateObserver) } @After fun tearDown() { - notificationViewModel.notificationUiState.removeObserver(notificationUiStateObserver) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt index 0ce537153..9353a64da 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt @@ -3,8 +3,11 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.* import org.junit.runner.RunWith @@ -39,8 +42,6 @@ class RecentTransactionViewModelTest { @Mock lateinit var recentTransactionRepositoryImp: RecentTransactionRepository - @Mock - lateinit var recentTransactionUiStateObserver: Observer @Mock lateinit var type: Type @@ -55,11 +56,10 @@ class RecentTransactionViewModelTest { fun setUp() { MockitoAnnotations.openMocks(this) viewModel = RecentTransactionViewModel(recentTransactionRepositoryImp) - viewModel.recentTransactionUiState.observeForever(recentTransactionUiStateObserver) } @Test - fun loadRecentTransaction_success_with_no_empty_transactions() = runBlocking { + fun loadRecentTransaction_success_with_no_empty_transactions() = runTest { val offset = 0 val limit = 50 @@ -74,22 +74,20 @@ class RecentTransactionViewModelTest { submittedOnDate = listOf(2023, 7, 9), reversed = false ) - val transactions: Page = + val transactions: Page = Page(totalFilteredRecords = 1, pageItems = listOf(transaction)) `when`(recentTransactionRepositoryImp.recentTransactions(offset, limit)) - .thenReturn(Response.success(transactions)) - - viewModel.loadRecentTransactions(loadmore = false, offset) - - verify(recentTransactionUiStateObserver).onChanged(RecentTransactionUiState.Loading) - Assert.assertEquals( - transactions.pageItems.let { RecentTransactionUiState.RecentTransactions(it) }, - viewModel.recentTransactionUiState.value - ) + .thenReturn(flowOf(transactions)) + viewModel.recentTransactionUiState.test { + viewModel.loadRecentTransactions(loadmore = false, offset) + assertEquals(RecentTransactionUiState.Initial, awaitItem()) + assertEquals(RecentTransactionUiState.Loading, awaitItem()) + assertEquals(transactions.pageItems.let { RecentTransactionUiState.RecentTransactions(it) }, awaitItem()) + } } @Test - fun loadRecentTransaction_success_with_empty_transactions() = runBlocking { + fun loadRecentTransaction_success_with_empty_transactions() = runTest { val offset = 0 val limit = 50 @@ -104,22 +102,21 @@ class RecentTransactionViewModelTest { submittedOnDate = listOf(2023, 7, 9), reversed = false ) - val transactions: Page = + val transactions: Page = Page(totalFilteredRecords = 0, pageItems = listOf(transaction)) `when`(recentTransactionRepositoryImp.recentTransactions(offset, limit)) - .thenReturn(Response.success(transactions)) - - viewModel.loadRecentTransactions(loadmore = false, offset) - - verify(recentTransactionUiStateObserver).onChanged(RecentTransactionUiState.Loading) - Assert.assertEquals( - RecentTransactionUiState.EmptyTransaction, - viewModel.recentTransactionUiState.value - ) + .thenReturn(flowOf(transactions)) + viewModel.recentTransactionUiState.test { + viewModel.loadRecentTransactions(loadmore = false, offset) + assertEquals(RecentTransactionUiState.Initial, awaitItem()) + assertEquals(RecentTransactionUiState.Loading, awaitItem()) + assertEquals(RecentTransactionUiState.EmptyTransaction, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @Test - fun loadRecentTransaction_success_with_load_more_transactions() = runBlocking { + fun loadRecentTransaction_success_with_load_more_transactions() = runTest { val offset = 0 val limit = 50 @@ -134,36 +131,35 @@ class RecentTransactionViewModelTest { submittedOnDate = listOf(2023, 7, 9), reversed = false ) - val transactions: Page = + val transactions: Page = Page(totalFilteredRecords = 1, pageItems = listOf(transaction)) `when`(recentTransactionRepositoryImp.recentTransactions(offset, limit)) - .thenReturn(Response.success(transactions)) - - viewModel.loadRecentTransactions(loadmore = true, offset) - - verify(recentTransactionUiStateObserver).onChanged(RecentTransactionUiState.Loading) - Assert.assertEquals( - transactions.pageItems.let { RecentTransactionUiState.LoadMoreRecentTransactions(it) }, - viewModel.recentTransactionUiState.value - ) + .thenReturn(flowOf(transactions)) + + viewModel.recentTransactionUiState.test { + viewModel.loadRecentTransactions(loadmore = false, offset) + assertEquals(RecentTransactionUiState.Initial, awaitItem()) + assertEquals(RecentTransactionUiState.Loading, awaitItem()) + assertEquals(transactions.pageItems.let { RecentTransactionUiState.RecentTransactions(it) }, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun loadRecentTransaction_unsuccessful() = runBlocking { - `when`(recentTransactionRepositoryImp.recentTransactions(anyInt(), anyInt())).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) + @Test(expected = Exception::class) + fun loadRecentTransaction_unsuccessful() = runTest { + `when`(recentTransactionRepositoryImp.recentTransactions(anyInt(), anyInt())) + .thenThrow(Exception("Error occurred")) + viewModel.recentTransactionUiState.test { viewModel.loadRecentTransactions(false, 0) - - Assert.assertEquals( - RecentTransactionUiState.Error(R.string.recent_transactions), - viewModel.recentTransactionUiState.value - ) + assertEquals(RecentTransactionUiState.Initial, awaitItem()) + assertEquals(RecentTransactionUiState.Loading, awaitItem()) + assertEquals(RecentTransactionUiState.Error(R.string.recent_transactions), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - viewModel.recentTransactionUiState.removeObserver(recentTransactionUiStateObserver) - } + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt index aa06e4b4b..b31ce1bdb 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/RegistrationViewModelTest.kt @@ -3,8 +3,11 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.* import org.junit.runner.RunWith @@ -35,22 +38,13 @@ class RegistrationViewModelTest { @Mock lateinit var userAuthRepositoryImp: UserAuthRepository - @Mock - lateinit var registrationUiStateObserver: Observer - - @Mock - lateinit var registrationVerificationUiStateObserver: Observer - private lateinit var registrationViewModel: RegistrationViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) registrationViewModel = RegistrationViewModel(userAuthRepositoryImp) - registrationViewModel.registrationUiState.observeForever(registrationUiStateObserver) - registrationViewModel.registrationVerificationUiState.observeForever( - registrationVerificationUiStateObserver - ) + } @Test @@ -117,7 +111,7 @@ class RegistrationViewModelTest { @Test fun testRegisterUser_SuccessfulRegistrationReceivedFromRepository_ReturnsRegistrationSuccessful() = - runBlocking { + runTest { val responseBody = Mockito.mock(ResponseBody::class.java) Mockito.`when`( userAuthRepositoryImp.registerUser( @@ -130,27 +124,28 @@ class RegistrationViewModelTest { Mockito.anyString(), Mockito.anyString(), ) - ).thenReturn(Response.success(responseBody)) - - registrationViewModel.registerUser( - "accountNumber", - "authMode", - "email", - "firstName", - "lastName", - "mobileNumber", - "password", - "userName" - ) - - Mockito.verify(registrationUiStateObserver).onChanged(RegistrationUiState.Loading) - Mockito.verify(registrationUiStateObserver).onChanged(RegistrationUiState.Success) - Mockito.verifyNoMoreInteractions(registrationUiStateObserver) + ).thenReturn(flowOf(responseBody)) + registrationViewModel.registrationUiState.test { + registrationViewModel.registerUser( + "accountNumber", + "authMode", + "email", + "firstName", + "lastName", + "mobileNumber", + "password", + "username" + ) + assertEquals(RegistrationUiState.Initial, awaitItem()) + assertEquals(RegistrationUiState.Loading, awaitItem()) + assertEquals(RegistrationUiState.Success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test + @Test(expected = Exception::class) fun testRegisterUser_UnsuccessfulRegistrationReceivedFromRepository_ReturnsRegistrationUnsuccessful() = - runBlocking { + runTest { Mockito.`when`( userAuthRepositoryImp.registerUser( Mockito.anyString(), @@ -162,60 +157,59 @@ class RegistrationViewModelTest { Mockito.anyString(), Mockito.anyString() ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - registrationViewModel.registerUser( - "accountNumber", - "authMode", - "email", - "firstName", - "lastName", - "mobileNumber", - "password", - "username" - ) - - Mockito.verify(registrationUiStateObserver).onChanged(RegistrationUiState.Loading) - Mockito.verify(registrationUiStateObserver) - .onChanged(Mockito.any(RegistrationUiState.Error::class.java)) - Mockito.verifyNoMoreInteractions(registrationUiStateObserver) + ).thenThrow(Exception("Error occurred")) + registrationViewModel.registrationUiState.test { + registrationViewModel.registerUser( + "accountNumber", + "authMode", + "email", + "firstName", + "lastName", + "mobileNumber", + "password", + "username" + ) + assertEquals(RegistrationUiState.Initial, awaitItem()) + assertEquals(RegistrationUiState.Loading, awaitItem()) + assertEquals(RegistrationUiState.Error(0), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @Test fun testVerifyUser_SuccessfulRegistrationVerificationReceivedFromRepository_ReturnsRegistrationVerificationSuccessful() = - runBlocking { + runTest { Mockito.`when`( userAuthRepositoryImp.verifyUser(Mockito.anyString(), Mockito.anyString()) - ).thenReturn(Response.success(Mockito.mock(ResponseBody::class.java))) + ).thenReturn(flowOf(Mockito.mock(ResponseBody::class.java))) + registrationViewModel.registrationVerificationUiState.test { registrationViewModel.verifyUser("authenticationToken", "requestId") - - Mockito.verify(registrationVerificationUiStateObserver) - .onChanged(RegistrationUiState.Loading) - Mockito.verify(registrationVerificationUiStateObserver) - .onChanged(RegistrationUiState.Success) - Mockito.verifyNoMoreInteractions(registrationUiStateObserver) + assertEquals(RegistrationUiState.Initial,awaitItem()) + assertEquals(RegistrationUiState.Loading,awaitItem()) + assertEquals(RegistrationUiState.Success,awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test + @Test(expected = Exception::class) fun testVerifyUser_UnsuccessfulRegistrationVerificationReceivedFromRepository_ReturnsRegistrationVerificationUnsuccessful() = - runBlocking { + runTest { + val errorResponse = Exception("Error occurred") Mockito.`when`( userAuthRepositoryImp.verifyUser(Mockito.anyString(), Mockito.anyString()) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - registrationViewModel.verifyUser("authenticationToken", "requestId") - - Mockito.verify(registrationVerificationUiStateObserver) - .onChanged(RegistrationUiState.Loading) - Mockito.verifyNoMoreInteractions(registrationUiStateObserver) + ).thenThrow(errorResponse) + registrationViewModel.registrationVerificationUiState.test{ + registrationViewModel.verifyUser("authenticationToken", "requestId") + assertEquals(RegistrationUiState.Initial,awaitItem()) + assertEquals(RegistrationUiState.Loading,awaitItem()) + assertEquals(RegistrationUiState.Error(0),awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - registrationViewModel.registrationUiState.removeObserver(registrationUiStateObserver) - registrationViewModel.registrationVerificationUiState.removeObserver( - registrationVerificationUiStateObserver - ) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt index f04a22ad1..7aae5eb7d 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsDetailViewModelTest.kt @@ -3,8 +3,12 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -14,6 +18,7 @@ import org.junit.runner.RunWith import org.mifos.mobile.FakeRemoteDataSource import org.mifos.mobile.repositories.SavingsAccountRepository import org.mifos.mobile.ui.savings_account.SavingAccountsDetailViewModel +import org.mifos.mobile.ui.savings_account.SavingsAccountDetailUiState import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.utils.Constants import org.mifos.mobile.utils.SavingsAccountUiState @@ -40,8 +45,6 @@ class SavingAccountsDetailViewModelTest { @Mock lateinit var savingsAccountRepositoryImp: SavingsAccountRepository - @Mock - lateinit var savingAccountsDetailUiStateObserver: Observer private lateinit var savingAccountsDetailViewModel: SavingAccountsDetailViewModel private val mockAccountId = 1L @@ -52,53 +55,50 @@ class SavingAccountsDetailViewModelTest { fun setUp() { MockitoAnnotations.openMocks(this) savingAccountsDetailViewModel = SavingAccountsDetailViewModel(savingsAccountRepositoryImp) - savingAccountsDetailViewModel.savingAccountsDetailUiState.observeForever( - savingAccountsDetailUiStateObserver - ) + } @Test fun testLoadSavingsWithAssociations_SuccessResponseFromRepository_ReturnsSuccessLoadingSavingsWithAssociations() = - runBlocking { + runTest { Mockito.`when`( savingsAccountRepositoryImp.getSavingsWithAssociations( mockAccountId, mockAssociationType ) - ).thenReturn(Response.success(mockSavingsWithAssociations)) + ).thenReturn(flowOf(mockSavingsWithAssociations)) savingAccountsDetailViewModel.loadSavingsWithAssociations(mockAccountId) - - Mockito.verify(savingAccountsDetailUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingAccountsDetailUiStateObserver).onChanged( - SavingsAccountUiState.SuccessLoadingSavingsWithAssociations( + advanceUntilIdle() + assertEquals( + SavingsAccountDetailUiState.Success( mockSavingsWithAssociations - ) + ), + savingAccountsDetailViewModel.savingAccountsDetailUiState.value ) - Mockito.verifyNoMoreInteractions(savingAccountsDetailUiStateObserver) - } - @Test - fun testLoadSavingsWithAssociations_ErrorResponseFromRepository_ReturnsError() = runBlocking { - Mockito.`when`( - savingsAccountRepositoryImp.getSavingsWithAssociations( - mockAccountId, - mockAssociationType - ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) + } - savingAccountsDetailViewModel.loadSavingsWithAssociations(mockAccountId) + @Test(expected = Exception::class) + fun testLoadSavingsWithAssociations_ErrorResponseFromRepository_ReturnsError() = + runTest { + Mockito.`when`( + savingsAccountRepositoryImp.getSavingsWithAssociations( + mockAccountId, + mockAssociationType + ) + ).thenThrow(Exception("Error occurred")) + savingAccountsDetailViewModel.loadSavingsWithAssociations(mockAccountId) + advanceUntilIdle() + assertEquals( + SavingsAccountUiState.Error, + savingAccountsDetailViewModel.savingAccountsDetailUiState.value + ) + } - Mockito.verify(savingAccountsDetailUiStateObserver).onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingAccountsDetailUiStateObserver).onChanged(SavingsAccountUiState.Error) - Mockito.verifyNoMoreInteractions(savingAccountsDetailUiStateObserver) - } @After fun tearDown() { - savingAccountsDetailViewModel.savingAccountsDetailUiState.removeObserver( - savingAccountsDetailUiStateObserver - ) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt index 02008a18f..32208c593 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/SavingAccountsTransactionViewModelTest.kt @@ -3,8 +3,12 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -39,9 +43,6 @@ class SavingAccountsTransactionViewModelTest { @Mock lateinit var savingsAccountRepositoryImp: SavingsAccountRepository - @Mock - lateinit var savingAccountsTransactionUiStateObserver: Observer - private lateinit var savingAccountsTransactionViewModel: SavingAccountsTransactionViewModel private val mockAccountId = 1L private val mockAssociationType = Constants.TRANSACTIONS @@ -52,54 +53,46 @@ class SavingAccountsTransactionViewModelTest { MockitoAnnotations.openMocks(this) savingAccountsTransactionViewModel = SavingAccountsTransactionViewModel(savingsAccountRepositoryImp) - savingAccountsTransactionViewModel.savingAccountsTransactionUiState.observeForever( - savingAccountsTransactionUiStateObserver - ) + } @Test fun testLoadSavingsWithAssociations_SuccessReceivedFromRepository_ReturnSuccessLoadingSavingsWithAssociations() = - runBlocking { + runTest{ Mockito.`when`( savingsAccountRepositoryImp.getSavingsWithAssociations( mockAccountId, mockAssociationType ) - ).thenReturn(Response.success(mockSavingsWithAssociations)) - - savingAccountsTransactionViewModel.loadSavingsWithAssociations(mockAccountId) - - Mockito.verify(savingAccountsTransactionUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingAccountsTransactionUiStateObserver).onChanged( - SavingsAccountUiState.SuccessLoadingSavingsWithAssociations( - mockSavingsWithAssociations - ) - ) - Mockito.verifyNoMoreInteractions(savingAccountsTransactionUiStateObserver) + ).thenReturn(flowOf(mockSavingsWithAssociations)) + savingAccountsTransactionViewModel.savingAccountsTransactionUiState.test { + savingAccountsTransactionViewModel.loadSavingsWithAssociations(mockAccountId) + assertEquals(SavingsAccountUiState.Initial,awaitItem()) + assertEquals(SavingsAccountUiState.Loading,awaitItem()) + assertEquals(SavingsAccountUiState.SuccessLoadingSavingsWithAssociations(mockSavingsWithAssociations),awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test - fun testLoadSavingsWithAssociations_ErrorResponseFromRepository_ReturnsError() = runBlocking { + @Test(expected = Exception::class) + fun testLoadSavingsWithAssociations_ErrorResponseFromRepository_ReturnsError() = runTest{ Mockito.`when`( savingsAccountRepositoryImp.getSavingsWithAssociations( mockAccountId, mockAssociationType ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - savingAccountsTransactionViewModel.loadSavingsWithAssociations(mockAccountId) - Mockito.verify(savingAccountsTransactionUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingAccountsTransactionUiStateObserver) - .onChanged(SavingsAccountUiState.Error) - Mockito.verifyNoMoreInteractions(savingAccountsTransactionUiStateObserver) + ).thenThrow(Exception("Error occurred while fetching data from server")) + savingAccountsTransactionViewModel.savingAccountsTransactionUiState.test { + savingAccountsTransactionViewModel.loadSavingsWithAssociations(mockAccountId) + assertEquals(SavingsAccountUiState.Initial,awaitItem()) + assertEquals(SavingsAccountUiState.Loading,awaitItem()) + assertEquals(SavingsAccountUiState.Error,awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - savingAccountsTransactionViewModel.savingAccountsTransactionUiState.removeObserver( - savingAccountsTransactionUiStateObserver - ) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt index 440bb2d17..e834402a5 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountApplicationViewModelTest.kt @@ -3,24 +3,34 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mifos.mobile.api.local.PreferencesHelper import org.mifos.mobile.models.accounts.savings.SavingsAccountApplicationPayload import org.mifos.mobile.models.accounts.savings.SavingsAccountUpdatePayload +import org.mifos.mobile.models.accounts.savings.SavingsWithAssociations import org.mifos.mobile.models.templates.savings.SavingsAccountTemplate import org.mifos.mobile.repositories.SavingsAccountRepository import org.mifos.mobile.ui.enums.SavingsAccountState +import org.mifos.mobile.ui.savings_account_application.SavingsAccountApplicationUiState import org.mifos.mobile.ui.savings_account_application.SavingsAccountApplicationViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.utils.SavingsAccountUiState import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import retrofit2.Response @@ -40,9 +50,10 @@ class SavingsAccountApplicationViewModelTest { @Mock lateinit var savingsAccountRepositoryImp: SavingsAccountRepository - + @Mock - lateinit var savingsAccountApplicationUiStateObserver: Observer + lateinit var preferenseHelper : PreferencesHelper + private lateinit var savingsAccountApplicationViewModel: SavingsAccountApplicationViewModel private val mockClientId = 1L @@ -52,79 +63,75 @@ class SavingsAccountApplicationViewModelTest { fun setUp() { MockitoAnnotations.openMocks(this) savingsAccountApplicationViewModel = - SavingsAccountApplicationViewModel(savingsAccountRepositoryImp) - savingsAccountApplicationViewModel.savingsAccountApplicationUiState.observeForever( - savingsAccountApplicationUiStateObserver - ) + SavingsAccountApplicationViewModel(savingsAccountRepositoryImp, + preferenseHelper) + } @Test fun testLoadSavingsAccountApplicationTemplate_InputCreateStateSuccessResponseFromRepository_ReturnsShowUserInterfaceSavingAccountApplication() = - runBlocking { + runTest { val mockState = SavingsAccountState.CREATE + val clientId = 1L val mockTemplate = Mockito.mock(SavingsAccountTemplate::class.java) Mockito.`when`( savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId) - ).thenReturn(Response.success(mockTemplate)) - - savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate( - mockClientId, - mockState - ) - - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver).onChanged( - SavingsAccountUiState.ShowUserInterfaceSavingAccountApplication(mockTemplate) + ).thenReturn(flowOf(mockTemplate)) + `when`(preferenseHelper.clientId).thenReturn(clientId) + + savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate() + advanceUntilIdle() + assertEquals( + SavingsAccountApplicationUiState.ShowUserInterface(mockTemplate, mockState), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value ) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) - } + } @Test fun testLoadSavingsAccountApplicationTemplate_InputUpdateStateSuccessResponseFromRepository_ReturnsShowUserInterfaceSavingAccountUpdate() = - runBlocking { + runTest { val mockState = SavingsAccountState.UPDATE val mockTemplate = Mockito.mock(SavingsAccountTemplate::class.java) + val mockPayload = Mockito.mock(SavingsAccountUpdatePayload::class.java) + val mockResponse = Mockito.mock(ResponseBody::class.java) + val mockSavingsWithAssociations = Mockito.mock(SavingsWithAssociations::class.java) Mockito.`when`( savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId) - ).thenReturn(Response.success(mockTemplate)) - - savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate( - mockClientId, - mockState + ).thenReturn(flowOf(mockTemplate)) + `when`(preferenseHelper.clientId).thenReturn(mockClientId) + `when`(savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId)) + .thenReturn(flow { emit(mockTemplate) }) + // Set state to UPDATE + savingsAccountApplicationViewModel.setSavingsAccountState(SavingsAccountState.UPDATE) + savingsAccountApplicationViewModel.setSavingsWithAssociations(mockSavingsWithAssociations) + savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate() + advanceUntilIdle() + + assertEquals( + SavingsAccountApplicationUiState.ShowUserInterface(mockTemplate,mockState), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value ) + } - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.ShowUserInterfaceSavingAccountUpdate(mockTemplate)) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) - } - - @Test + @Test(expected = Exception::class) fun testLoadSavingsAccountApplicationTemplate_ErrorResponseFromRepository_ReturnsErrorMessage() = - runBlocking { - val errorResponse = RuntimeException("Loading Failed") + runTest { + val errorResponse = Exception("Loading Failed") val mockState = SavingsAccountState.UPDATE Mockito.`when`( savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(mockClientId) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate( - mockClientId, - mockState + ).thenThrow(errorResponse) + savingsAccountApplicationViewModel.loadSavingsAccountApplicationTemplate() + advanceUntilIdle() + assertEquals( + SavingsAccountUiState.ErrorMessage(errorResponse), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value ) - - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.ErrorMessage(errorResponse)) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) - } + } @Test fun testSubmitSavingsAccountApplication_SuccessResponseFromRepository_ReturnsHideProgress() = - runBlocking { + runTest { val responseBody = Mockito.mock(ResponseBody::class.java) val mockSavingsAccountApplicationPayload = Mockito.mock(SavingsAccountApplicationPayload::class.java) @@ -132,65 +139,64 @@ class SavingsAccountApplicationViewModelTest { savingsAccountRepositoryImp.submitSavingAccountApplication( mockSavingsAccountApplicationPayload ) - ).thenReturn(Response.success(responseBody)) + ).thenReturn(flowOf(responseBody)) + savingsAccountApplicationViewModel.submitSavingsAccountApplication( mockSavingsAccountApplicationPayload ) + advanceUntilIdle() + assertEquals( + SavingsAccountApplicationUiState.Success(SavingsAccountState.CREATE), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value + ) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.HideProgress) - } + } - @Test + @Test(expected = Exception::class) fun testSubmitSavingsAccountApplication_SuccessResponseFromRepository_ReturnsSavingsAccountUpdateSuccess() = - runBlocking { + runTest { + val errorResponse = Exception("Submitting Failed") val mockSavingsAccountApplicationPayload = Mockito.mock(SavingsAccountApplicationPayload::class.java) Mockito.`when`( savingsAccountRepositoryImp.submitSavingAccountApplication( mockSavingsAccountApplicationPayload ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - + ).thenThrow(errorResponse) savingsAccountApplicationViewModel.submitSavingsAccountApplication( mockSavingsAccountApplicationPayload ) + advanceUntilIdle() + assertEquals( + SavingsAccountUiState.ErrorMessage(errorResponse), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value + ) + } - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.SavingsAccountApplicationSuccess) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) - } - - @Test + @Test(expected = Exception::class) fun testSubmitSavingsAccountApplication_ErrorResponseFromRepository_ReturnsErrorMessage() = - runBlocking { - val errorResponse = RuntimeException("Submitting Failed") + runTest { + val errorResponse = Exception("Submitting Failed") val mockSavingsAccountApplicationPayload = Mockito.mock(SavingsAccountApplicationPayload::class.java) Mockito.`when`( savingsAccountRepositoryImp.submitSavingAccountApplication( mockSavingsAccountApplicationPayload ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - + ).thenThrow(errorResponse) savingsAccountApplicationViewModel.submitSavingsAccountApplication( mockSavingsAccountApplicationPayload ) - - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.ErrorMessage(errorResponse)) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) + advanceUntilIdle() + assertEquals( + SavingsAccountUiState.ErrorMessage(errorResponse), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value + ) } @Test - fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsHideProgress() = runBlocking { + fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsHideProgress() =runTest { val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java) val responseBody = Mockito.mock(ResponseBody::class.java) Mockito.`when`( @@ -198,22 +204,23 @@ class SavingsAccountApplicationViewModelTest { mockAccountId, mockSavingsAccountUpdatePayload ) - ).thenReturn(Response.success(responseBody)) - + ).thenReturn(flowOf(responseBody)) + savingsAccountApplicationViewModel.setSavingsAccountState(SavingsAccountState.UPDATE) savingsAccountApplicationViewModel.updateSavingsAccount( mockAccountId, mockSavingsAccountUpdatePayload ) - - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.HideProgress) + advanceUntilIdle() + assertEquals( + SavingsAccountApplicationUiState.Success(SavingsAccountState.UPDATE), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value + ) } - @Test + @Test(expected = Exception::class) fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsSavingsAccountUpdateSuccess() = - runBlocking { + runTest { + val errorResponse = Exception("Update Failed") val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java) Mockito.`when`( @@ -221,47 +228,41 @@ class SavingsAccountApplicationViewModelTest { mockAccountId, mockSavingsAccountUpdatePayload ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - + ).thenThrow(errorResponse) savingsAccountApplicationViewModel.updateSavingsAccount( mockAccountId, mockSavingsAccountUpdatePayload ) + advanceUntilIdle() + assertEquals( + SavingsAccountUiState.ErrorMessage(errorResponse), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value + ) + } - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.SavingsAccountUpdateSuccess) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) - } - - @Test - fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsErrorMessage() = runBlocking { - val errorResponse = RuntimeException("Update Failed") + @Test(expected = Exception::class) + fun testUpdateSavingsAccount_SuccessResponseFromRepository_ReturnsErrorMessage() =runTest { + val errorResponse = Exception("Update Failed") val mockSavingsAccountUpdatePayload = Mockito.mock(SavingsAccountUpdatePayload::class.java) Mockito.`when`( savingsAccountRepositoryImp.updateSavingsAccount( mockAccountId, mockSavingsAccountUpdatePayload ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - + ).thenThrow(errorResponse) savingsAccountApplicationViewModel.updateSavingsAccount( mockAccountId, mockSavingsAccountUpdatePayload ) - - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountApplicationUiStateObserver) - .onChanged(SavingsAccountUiState.ErrorMessage(errorResponse)) - Mockito.verifyNoMoreInteractions(savingsAccountApplicationUiStateObserver) + advanceUntilIdle() + assertEquals( + SavingsAccountUiState.ErrorMessage(errorResponse), + savingsAccountApplicationViewModel.savingsAccountApplicationUiState.value + ) } @After fun tearDown() { - savingsAccountApplicationViewModel.savingsAccountApplicationUiState.removeObserver( - savingsAccountApplicationUiStateObserver - ) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt index 6e847ee8f..0a345458c 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/SavingsAccountWithdrawViewModelTest.kt @@ -3,8 +3,12 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -37,9 +41,6 @@ class SavingsAccountWithdrawViewModelTest { @Mock lateinit var savingsAccountRepositoryImp: SavingsAccountRepository - @Mock - lateinit var savingsAccountWithdrawUiStateObserver: Observer - private lateinit var savingsAccountWithdrawViewModel: SavingsAccountWithdrawViewModel private val mockAccountId = "1" @@ -48,14 +49,12 @@ class SavingsAccountWithdrawViewModelTest { MockitoAnnotations.openMocks(this) savingsAccountWithdrawViewModel = SavingsAccountWithdrawViewModel(savingsAccountRepositoryImp) - savingsAccountWithdrawViewModel.savingsAccountWithdrawUiState.observeForever( - savingsAccountWithdrawUiStateObserver - ) + } @Test fun testSubmitWithdrawSavingsAccount_SuccessReceivedFromRepository_ReturnsSavingsAccountWithdrawSuccess() = - runBlocking { + runTest{ val mockSavingsAccountWithdrawPayload = Mockito.mock(SavingsAccountWithdrawPayload::class.java) val responseBody = Mockito.mock(ResponseBody::class.java) @@ -64,49 +63,45 @@ class SavingsAccountWithdrawViewModelTest { mockAccountId, mockSavingsAccountWithdrawPayload ) - ).thenReturn(Response.success(responseBody)) - - savingsAccountWithdrawViewModel.submitWithdrawSavingsAccount( - mockAccountId, - mockSavingsAccountWithdrawPayload - ) - - Mockito.verify(savingsAccountWithdrawUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountWithdrawUiStateObserver) - .onChanged(SavingsAccountUiState.SavingsAccountWithdrawSuccess) - Mockito.verifyNoMoreInteractions(savingsAccountWithdrawUiStateObserver) + ).thenReturn(flowOf(responseBody)) + savingsAccountWithdrawViewModel.savingsAccountWithdrawUiState.test { + savingsAccountWithdrawViewModel.submitWithdrawSavingsAccount( + mockAccountId, + mockSavingsAccountWithdrawPayload + ) + assertEquals(SavingsAccountUiState.Initial, awaitItem()) + assertEquals(SavingsAccountUiState.Loading, awaitItem()) + assertEquals(SavingsAccountUiState.SavingsAccountWithdrawSuccess, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test + @Test(expected = Exception::class) fun testSubmitWithdrawSavingsAccount_ErrorResponseFromRepository_ReturnsErrorMessage() = - runBlocking { + runTest{ val mockSavingsAccountWithdrawPayload = Mockito.mock(SavingsAccountWithdrawPayload::class.java) - val errorResponse = RuntimeException("Submit Failed") + val errorResponse = Exception("Submit Failed") Mockito.`when`( savingsAccountRepositoryImp.submitWithdrawSavingsAccount( mockAccountId, mockSavingsAccountWithdrawPayload ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - savingsAccountWithdrawViewModel.submitWithdrawSavingsAccount( - mockAccountId, - mockSavingsAccountWithdrawPayload - ) - - Mockito.verify(savingsAccountWithdrawUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsAccountWithdrawUiStateObserver) - .onChanged(SavingsAccountUiState.ErrorMessage(errorResponse)) - Mockito.verifyNoMoreInteractions(savingsAccountWithdrawUiStateObserver) + ).thenThrow(errorResponse ) + savingsAccountWithdrawViewModel.savingsAccountWithdrawUiState.test { + savingsAccountWithdrawViewModel.submitWithdrawSavingsAccount( + mockAccountId, + mockSavingsAccountWithdrawPayload + ) + assertEquals(SavingsAccountUiState.Initial, awaitItem()) + assertEquals(SavingsAccountUiState.Loading, awaitItem()) + assertEquals(SavingsAccountUiState.ErrorMessage(errorResponse), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - savingsAccountWithdrawViewModel.savingsAccountWithdrawUiState.removeObserver( - savingsAccountWithdrawUiStateObserver - ) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt index 66c56976a..b79f8d83b 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/SavingsMakeTransferViewModelTest.kt @@ -3,8 +3,11 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest import okhttp3.ResponseBody import org.junit.After import org.junit.Before @@ -38,59 +41,52 @@ class SavingsMakeTransferViewModelTest { @Mock lateinit var savingsAccountRepositoryImp: SavingsAccountRepository - @Mock - lateinit var savingsMakeTransferUiStateObserver: Observer - private lateinit var savingsMakeTransferViewModel: SavingsMakeTransferViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) savingsMakeTransferViewModel = SavingsMakeTransferViewModel(savingsAccountRepositoryImp) - savingsMakeTransferViewModel.savingsMakeTransferUiState.observeForever( - savingsMakeTransferUiStateObserver - ) + } @Test fun testLoanAccountTransferTemplate_SuccessResponseFromRepository_ReturnsShowSavingsAccountTemplate() = - runBlocking { + runTest { val mockAccountOptionsTemplate = Mockito.mock(AccountOptionsTemplate::class.java) Mockito.`when`( savingsAccountRepositoryImp.loanAccountTransferTemplate() - ).thenReturn(Response.success(mockAccountOptionsTemplate)) + ).thenReturn(flowOf( mockAccountOptionsTemplate)) + + savingsMakeTransferViewModel.savingsMakeTransferUiState.test { - savingsMakeTransferViewModel.loanAccountTransferTemplate() + savingsMakeTransferViewModel.loanAccountTransferTemplate() + assertEquals(SavingsAccountUiState.Initial, awaitItem()) + assertEquals(SavingsAccountUiState.Loading, awaitItem()) + assertEquals(SavingsAccountUiState.ShowSavingsAccountTemplate(mockAccountOptionsTemplate), awaitItem()) + cancelAndIgnoreRemainingEvents() - Mockito.verify(savingsMakeTransferUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsMakeTransferUiStateObserver).onChanged( - SavingsAccountUiState.ShowSavingsAccountTemplate(mockAccountOptionsTemplate) - ) - Mockito.verifyNoMoreInteractions(savingsMakeTransferUiStateObserver) + } } - @Test + @Test(expected = Exception::class) fun testLoanAccountTransferTemplate_ErrorResponseFromRepository_ReturnsErrorMessage() = - runBlocking { - val errorResponse = RuntimeException("Loading Failed") + runTest { + val errorResponse = Exception("Loading Failed") Mockito.`when`( savingsAccountRepositoryImp.loanAccountTransferTemplate() - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - savingsMakeTransferViewModel.loanAccountTransferTemplate() - - Mockito.verify(savingsMakeTransferUiStateObserver) - .onChanged(SavingsAccountUiState.Loading) - Mockito.verify(savingsMakeTransferUiStateObserver) - .onChanged(SavingsAccountUiState.ErrorMessage(errorResponse)) - Mockito.verifyNoMoreInteractions(savingsMakeTransferUiStateObserver) + ).thenThrow(errorResponse) + savingsMakeTransferViewModel.savingsMakeTransferUiState.test { + savingsMakeTransferViewModel.loanAccountTransferTemplate() + assertEquals(SavingsAccountUiState.Initial, awaitItem()) + assertEquals(SavingsAccountUiState.Loading, awaitItem()) + assertEquals(SavingsAccountUiState.ErrorMessage(errorResponse), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - savingsMakeTransferViewModel.savingsMakeTransferUiState.removeObserver( - savingsMakeTransferUiStateObserver - ) + } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt index 9d234c9d6..bcdb83651 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/ThirdPartyTransferViewModelTest.kt @@ -3,8 +3,10 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import okhttp3.ResponseBody import org.junit.After @@ -14,11 +16,13 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mifos.mobile.R +import org.mifos.mobile.models.AccountOptionAndBeneficiary import org.mifos.mobile.models.beneficiary.Beneficiary import org.mifos.mobile.models.payload.AccountDetail import org.mifos.mobile.models.templates.account.AccountOption import org.mifos.mobile.models.templates.account.AccountOptionsTemplate import org.mifos.mobile.models.templates.account.AccountType +import org.mifos.mobile.repositories.BeneficiaryRepositoryImp import org.mifos.mobile.repositories.ThirdPartyTransferRepositoryImp import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.utils.ThirdPartyTransferUiState @@ -47,17 +51,18 @@ class ThirdPartyTransferViewModelTest { lateinit var thirdPartyTransferRepositoryImp: ThirdPartyTransferRepositoryImp @Mock - lateinit var thirdPartyTransferUiStateObserver: Observer + lateinit var beneficiaryRepositoryImp: BeneficiaryRepositoryImp private lateinit var thirdPartyTransferViewModel: ThirdPartyTransferViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) - thirdPartyTransferViewModel = ThirdPartyTransferViewModel(thirdPartyTransferRepositoryImp) - thirdPartyTransferViewModel.thirdPartyTransferUiState.observeForever( - thirdPartyTransferUiStateObserver - ) + thirdPartyTransferViewModel = + ThirdPartyTransferViewModel( + thirdPartyTransferRepositoryImp, + beneficiaryRepositoryImp,) + } @Test @@ -67,40 +72,50 @@ class ThirdPartyTransferViewModelTest { val list2 = mock(Beneficiary::class.java) val beneficiaryListResult = listOf(list1, list2) - `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate()).thenReturn( - Response.success( + `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate()) + .thenReturn( + flowOf( templateResult ) ) - `when`(thirdPartyTransferRepositoryImp.beneficiaryList()).thenReturn( - Response.success( + `when`(beneficiaryRepositoryImp.beneficiaryList()) + .thenReturn( + flowOf( beneficiaryListResult ) ) - - thirdPartyTransferViewModel.loadTransferTemplate() + thirdPartyTransferViewModel.accountOptionAndBeneficiary = + AccountOptionAndBeneficiary( + templateResult, + beneficiaryListResult + ) + thirdPartyTransferViewModel.thirdPartyTransferUiState.test { + thirdPartyTransferViewModel.loadTransferTemplate() + assertEquals(ThirdPartyTransferUiState.Initial, awaitItem()) + assertEquals(ThirdPartyTransferUiState.Loading, awaitItem()) + assertEquals(ThirdPartyTransferUiState.ShowThirdPartyTransferTemplate(templateResult), awaitItem()) + assertEquals(ThirdPartyTransferUiState.ShowBeneficiaryList(beneficiaryListResult), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test + @Test(expected = Exception::class) fun testLoadTransferTemplate_Unsuccessful() = runBlocking { val errorMessage = R.string.error_fetching_third_party_transfer_template - `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate()).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - `when`(thirdPartyTransferRepositoryImp.beneficiaryList()).thenReturn( - Response.error(404, ResponseBody.create(null, "error")) - ) - - thirdPartyTransferViewModel.loadTransferTemplate() - - verify(thirdPartyTransferUiStateObserver).onChanged(ThirdPartyTransferUiState.Loading) - verify(thirdPartyTransferUiStateObserver).onChanged( - ThirdPartyTransferUiState.Error( - errorMessage - ) - ) - verifyNoMoreInteractions(thirdPartyTransferUiStateObserver) + `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate()). + thenThrow(Exception("Error fetching third party transfer template")) + `when`(thirdPartyTransferRepositoryImp.thirdPartyTransferTemplate()) + .thenThrow(Exception("Error fetching beneficiary list")) + `when`(beneficiaryRepositoryImp.beneficiaryList()) + .thenThrow(Exception("Error fetching beneficiary list")) + thirdPartyTransferViewModel.thirdPartyTransferUiState.test { + thirdPartyTransferViewModel.loadTransferTemplate() + assertEquals(ThirdPartyTransferUiState.Initial, awaitItem()) + assertEquals(ThirdPartyTransferUiState.Loading, awaitItem()) + assertEquals(ThirdPartyTransferUiState.Error(errorMessage), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @Test @@ -189,8 +204,5 @@ class ThirdPartyTransferViewModelTest { @After fun tearDown() { - thirdPartyTransferViewModel.thirdPartyTransferUiState.removeObserver( - thirdPartyTransferUiStateObserver - ) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt index 9f6b5a270..c64cb7f0b 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/TransferProcessViewModelTest.kt @@ -2,9 +2,14 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import okhttp3.ResponseBody import org.junit.After @@ -35,20 +40,16 @@ class TransferProcessViewModelTest { @Mock lateinit var transferProcessImp: TransferRepositoryImp - @Mock - lateinit var transferUiStateObserver: Observer - private lateinit var viewModel: TransferProcessViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) viewModel = TransferProcessViewModel(transferProcessImp) - viewModel.transferUiState.observeForever(transferUiStateObserver) } @Test - fun makeTransfer_successful() = runBlocking { + fun makeTransfer_successful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) val responseBody = Mockito.mock(ResponseBody::class.java) Mockito.`when`( @@ -59,25 +60,26 @@ class TransferProcessViewModelTest { "dd MMMM yyyy", "en", "0000001", "0000002", TransferType.SELF ) - ).thenReturn(Response.success(responseBody)) - - viewModel.makeTransfer( - 1, 2, 3, - 4, 5, 6, 7, - 8, "06 July 2023 ", 100.0, "Transfer", - "dd MMMM yyyy", "en", "0000001", "0000002", - TransferType.SELF - ) - Mockito.verify(transferUiStateObserver).onChanged(TransferUiState.Loading) - Mockito.verify(transferUiStateObserver).onChanged(TransferUiState.TransferSuccess) - Mockito.verifyNoMoreInteractions(transferUiStateObserver) + ).thenReturn(flowOf(responseBody)) + viewModel.transferUiState.test { + viewModel.makeTransfer( + 1, 2, 3, + 4, 5, 6, 7, + 8, "06 July 2023 ", 100.0, "Transfer", + "dd MMMM yyyy", "en", "0000001", "0000002", + TransferType.SELF + ) + assertEquals(TransferUiState.Initial, awaitItem()) + assertEquals(TransferUiState.TransferSuccess, awaitItem()) + cancelAndIgnoreRemainingEvents() + } Dispatchers.resetMain() } - @Test - fun makeTransfer_unsuccessful() = runBlocking { + @Test(expected = Exception::class) + fun makeTransfer_unsuccessful() = runTest { Dispatchers.setMain(Dispatchers.Unconfined) - val error = RuntimeException("Savings Transfer Failed") + val error = Exception("Savings Transfer Failed") Mockito.`when`( transferProcessImp.makeTransfer( 1, 2, 3, @@ -86,23 +88,25 @@ class TransferProcessViewModelTest { "dd MMMM yyyy", "en", "0000001", "0000002", TransferType.SELF ) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) + ).thenThrow(error) + viewModel.transferUiState.test { - viewModel.makeTransfer( - 1, 2, 3, - 4, 5, 6, 7, - 8, "06 July 2023 ", 100.0, "Transfer", - "dd MMMM yyyy", "en", "0000001", "0000002", - TransferType.SELF - ) - Mockito.verify(transferUiStateObserver).onChanged(TransferUiState.Loading) - Mockito.verifyNoMoreInteractions(transferUiStateObserver) - Dispatchers.resetMain() + viewModel.makeTransfer( + 1, 2, 3, + 4, 5, 6, 7, + 8, "06 July 2023 ", 100.0, "Transfer", + "dd MMMM yyyy", "en", "0000001", "0000002", + TransferType.SELF + ) + assertEquals(TransferUiState.Initial, awaitItem()) + assertEquals(TransferUiState.Error(error), awaitItem()) + cancelAndIgnoreRemainingEvents() + Dispatchers.resetMain() + } } @After fun tearDown() { - viewModel.transferUiState.removeObserver(transferUiStateObserver) } -} \ No newline at end of file +} diff --git a/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt index 203f8516c..066830bf0 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt @@ -3,7 +3,10 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import app.cash.turbine.test +import junit.framework.TestCase.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import okhttp3.ResponseBody import org.junit.* @@ -38,9 +41,6 @@ class UpdatePasswordViewModelTest { @Mock lateinit var clientRepositoryImp: ClientRepository - @Mock - private lateinit var updatePasswordUiStateObserver: Observer - private lateinit var updatePasswordViewModel: UpdatePasswordViewModel @Before @@ -48,8 +48,7 @@ class UpdatePasswordViewModelTest { MockitoAnnotations.openMocks(this) updatePasswordViewModel = UpdatePasswordViewModel(userAuthRepositoryImp, clientRepositoryImp) - updatePasswordViewModel.updatePasswordUiState.observeForever(updatePasswordUiStateObserver) - } + } @Test fun testIsInputFieldEmpty_WithEmptyStringInput_ReturnsTrue() { @@ -92,31 +91,32 @@ class UpdatePasswordViewModelTest { val responseBody = Mockito.mock(ResponseBody::class.java) Mockito.`when`( userAuthRepositoryImp.updateAccountPassword(Mockito.anyString(), Mockito.anyString()) - ).thenReturn(Response.success(responseBody)) - - updatePasswordViewModel.updateAccountPassword("newPassword", "newPassword") - Mockito.verify(updatePasswordUiStateObserver).onChanged(RegistrationUiState.Loading) - Mockito.verify(updatePasswordUiStateObserver).onChanged(RegistrationUiState.Success) - Mockito.verify(clientRepositoryImp).updateAuthenticationToken("newPassword") - Mockito.verifyNoMoreInteractions(updatePasswordUiStateObserver) + ).thenReturn(flowOf(responseBody)) + updatePasswordViewModel.updatePasswordUiState.test { + updatePasswordViewModel.updateAccountPassword("newPassword", "newPassword") + assertEquals(RegistrationUiState.Initial, awaitItem()) + assertEquals(RegistrationUiState.Loading, awaitItem()) + assertEquals(RegistrationUiState.Success, awaitItem()) + cancelAndIgnoreRemainingEvents() + } } - @Test + @Test(expected = Exception::class) fun testUpdateAccountPassword_ErrorReceivedFromRepository_ReturnsError() = runBlocking { Mockito.`when`( userAuthRepositoryImp.updateAccountPassword(Mockito.anyString(), Mockito.anyString()) - ).thenReturn(Response.error(404, ResponseBody.create(null, "error"))) - - updatePasswordViewModel.updateAccountPassword("newPassword", "newPassword") - - Mockito.verify(updatePasswordUiStateObserver).onChanged(RegistrationUiState.Loading) - Mockito.verify(updatePasswordUiStateObserver) - .onChanged(Mockito.any(RegistrationUiState.Error::class.java)) - Mockito.verifyNoMoreInteractions(updatePasswordUiStateObserver) + ).thenThrow(Exception("Error updating password")) + + updatePasswordViewModel.updatePasswordUiState.test{ + updatePasswordViewModel.updateAccountPassword("newPassword", "newPassword") + assertEquals(RegistrationUiState.Initial, awaitItem()) + assertEquals(RegistrationUiState.Loading, awaitItem()) + assertEquals(RegistrationUiState.Error(0), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } @After fun tearDown() { - updatePasswordViewModel.updatePasswordUiState.removeObserver(updatePasswordUiStateObserver) } } \ No newline at end of file diff --git a/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt index bfb5bb1ff..8c51aa668 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt @@ -2,12 +2,14 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import app.cash.turbine.test import junit.framework.Assert import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test @@ -59,31 +61,33 @@ class UserDetailViewModelTest { } @Test - fun testLoadingUserDetails_Success(): Unit = runBlocking { + fun testLoadingUserDetails_Success(): Unit = runTest{ val mockClient = Mockito.mock(Client::class.java) Mockito.`when`(homeRepositoryImp.currentClient()).thenReturn(flowOf(mockClient)) - viewModel.userDetails - - viewModel.userDetailUiState.collect { value -> - Assert.assertTrue(value is UserDetailUiState.ShowUserDetails) - Assert.assertEquals(mockClient, (value as UserDetailUiState.ShowUserDetails).client) + viewModel.userDetailUiState.test { + viewModel.userDetails + assertEquals(UserDetailUiState.Loading ,awaitItem()) + assertEquals(UserDetailUiState.ShowUserDetails(mockClient), awaitItem()) + cancelAndIgnoreRemainingEvents() } } - @Test - fun testLoadingUserDetails_Error(): Unit = runBlocking { + @Test(expected = Exception::class) + fun testLoadingUserDetails_Error(): Unit = runTest{ val errorMessageResId = R.string.error_fetching_client - Mockito.`when`(homeRepositoryImp.currentClient()).thenThrow(RuntimeException()) + Mockito.`when`(homeRepositoryImp.currentClient()) + .thenThrow( Exception("Error fetching client details")) - viewModel.userDetails - viewModel.userDetailUiState.collect { value -> - assertTrue(value is UserDetailUiState.ShowError) - assertEquals(errorMessageResId, (value as UserDetailUiState.ShowError)) - } + viewModel.userDetailUiState.test { + viewModel.userDetails + assertEquals(UserDetailUiState.Loading ,awaitItem()) + assertEquals(UserDetailUiState.ShowError(errorMessageResId), awaitItem()) + cancelAndIgnoreRemainingEvents() + } } } \ No newline at end of file