From 14293254ec6d68b93bba50eea79df8808aec9de9 Mon Sep 17 00:00:00 2001 From: hmalik144 Date: Sun, 17 May 2020 17:56:26 +0100 Subject: [PATCH] Viewmodel unit tests updated --- .idea/caches/build_file_checksums.ser | Bin 537 -> 537 bytes app/build.gradle | 6 +- .../h_mal/easycc/ui/main/MainViewModel.kt | 2 +- .../appttude/h_mal/easycc/OneTimeObserver.kt | 21 ++ .../h_mal/easycc/ui/main/MainViewModelTest.kt | 233 ++++++++++++++++++ .../easycc/ui/widget/WidgetViewModelTest.kt | 118 +++++++++ .../appttude/h_mal/easycc/utils/TestUtils.kt | 9 + 7 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 app/src/test/java/com/appttude/h_mal/easycc/OneTimeObserver.kt create mode 100644 app/src/test/java/com/appttude/h_mal/easycc/ui/main/MainViewModelTest.kt create mode 100644 app/src/test/java/com/appttude/h_mal/easycc/ui/widget/WidgetViewModelTest.kt create mode 100644 app/src/test/java/com/appttude/h_mal/easycc/utils/TestUtils.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 2763ef00197a9a7b5a6fd323bca805ec368dc7ce..9290a046bff32622e97793788abaf9ca12ffa465 100644 GIT binary patch delta 35 tcmV+;0NnqX1epYom;@t^48)O~cMxlc@NnBAXd0fZki{oJHZPMS0rmld4u${# delta 35 tcmV+;0NnqX1epYom;@gOVjYp3cMy^p1NL^p$Gz6)ba_mw<3p1p0rvQ<50d}@ diff --git a/app/build.gradle b/app/build.gradle index bc52284..51314b8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,9 +19,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - testOptions { - unitTests.returnDefaultValues = true - } +// testOptions { +// unitTests.returnDefaultValues = true +// } dataBinding { enabled = true } diff --git a/app/src/main/java/com/appttude/h_mal/easycc/ui/main/MainViewModel.kt b/app/src/main/java/com/appttude/h_mal/easycc/ui/main/MainViewModel.kt index 6e2565a..19778f1 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/ui/main/MainViewModel.kt @@ -30,7 +30,7 @@ class MainViewModel( val operationStartedListener = MutableLiveData() val operationFinishedListener = MutableLiveData>() - private var conversionRate: Double = 0.00 + private var conversionRate: Double = 1.00 private fun getExchangeRate(){ operationStartedListener.postValue(true) diff --git a/app/src/test/java/com/appttude/h_mal/easycc/OneTimeObserver.kt b/app/src/test/java/com/appttude/h_mal/easycc/OneTimeObserver.kt new file mode 100644 index 0000000..d1eb6ae --- /dev/null +++ b/app/src/test/java/com/appttude/h_mal/easycc/OneTimeObserver.kt @@ -0,0 +1,21 @@ +package com.appttude.h_mal.easycc + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import androidx.lifecycle.Observer + +class OneTimeObserver(private val handler: (T) -> Unit) : Observer, LifecycleOwner { + private val lifecycle = LifecycleRegistry(this) + + init { + lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME) + } + + override fun getLifecycle(): Lifecycle = lifecycle + + override fun onChanged(t: T) { + handler(t) + lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/appttude/h_mal/easycc/ui/main/MainViewModelTest.kt b/app/src/test/java/com/appttude/h_mal/easycc/ui/main/MainViewModelTest.kt new file mode 100644 index 0000000..32a2bc0 --- /dev/null +++ b/app/src/test/java/com/appttude/h_mal/easycc/ui/main/MainViewModelTest.kt @@ -0,0 +1,233 @@ +package com.appttude.h_mal.easycc.ui.main + +import android.os.Bundle +import android.util.Log +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.appttude.h_mal.easycc.data.network.response.ResponseObject +import com.appttude.h_mal.easycc.data.repository.Repository +import com.appttude.h_mal.easycc.models.CurrencyObject +import kotlinx.coroutines.runBlocking +import org.junit.Before +import com.appttude.h_mal.easycc.utils.observeOnce +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations +import kotlin.time.seconds + +class MainViewModelTest { + + // Run tasks synchronously + @get:Rule + val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule() + + lateinit var viewModel: MainViewModel + + @Mock + lateinit var repository: Repository + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + viewModel = MainViewModel(repository) + } + + @Test + fun initiate_validBundleValues_successResponse() = runBlocking{ + //GIVEN + val currencyOne = "AUD - Australian Dollar" + val currencyTwo = "GBP - British Pound" + val bundle = mock(Bundle()::class.java) + val responseObject = mock(ResponseObject::class.java) + + //WHEN + Mockito.`when`(bundle.getString("parse_1")).thenReturn(currencyOne) + Mockito.`when`(bundle.getString("parse_2")).thenReturn(currencyTwo) + Mockito.`when`(repository.getData(currencyOne, currencyTwo)).thenReturn(responseObject) + + //THEN + viewModel.initiate(bundle) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(true, it.first) + Log.i("tag", "${it.first} ${it.second}") + assertNull(it.second) + } + } + + @Test + fun initiate_invalidBundleValues_successfulResponse() = runBlocking{ + //GIVEN + val currencyOne = "AUD - Australian Dollar" + val currencyTwo = "GBP - British Pound" + val pair = Pair(currencyOne, currencyTwo) + val responseObject = mock(ResponseObject::class.java) + + //WHEN + Mockito.`when`(repository.getConversionPair()).thenReturn(pair) + Mockito.`when`(repository.getData(currencyOne, currencyTwo)).thenReturn(responseObject) + + //THEN + viewModel.initiate(null) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(true, it.first) + assertNull(it.second) + } + } + + @Test + fun initiate_sameBundleValues_successfulResponse() = runBlocking{ + //GIVEN + val currencyOne = "AUD - Australian Dollar" + val bundle = mock(Bundle()::class.java) + + //WHEN + Mockito.`when`(bundle.getString("parse_1")).thenReturn(null) + Mockito.`when`(bundle.getString("parse_2")).thenReturn(null) + Mockito.`when`(repository.getConversionPair()).thenReturn(Pair(currencyOne, currencyOne)) + + //THEN + viewModel.initiate(bundle) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(true, it.first) + assertNull(it.second) + } + } + + @Test + fun initiate_invalidValues_unsuccessfulResponse() = runBlocking { + //GIVEN + val bundle = mock(Bundle()::class.java) + + //WHEN + Mockito.`when`(bundle.getString("parse_1")).thenReturn(null) + Mockito.`when`(bundle.getString("parse_2")).thenReturn(null) + Mockito.`when`(repository.getConversionPair()).thenReturn(Pair(null, null)) + + //THEN + viewModel.initiate(bundle) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(false, it.first) + assertEquals("Select currencies", it.second) + } + } + + + + @Test + fun setCurrencyName_validValues_successResponse() = runBlocking{ + //GIVEN + val currencyOne = "AUD - Australian Dollar" + val currencyTwo = "GBP - British Pound" + viewModel.rateIdTo = currencyTwo + val tag = "top" + val responseObject = mock(ResponseObject::class.java) + + //WHEN + Mockito.`when`(repository.getData(currencyOne, currencyTwo)).thenReturn(responseObject) + + //THEN + viewModel.setCurrencyName(tag, currencyOne) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(true, it.first) + Log.i("tag", "${it.first} ${it.second}") + assertNull(it.second) + } + } + + @Test + fun setCurrencyName_sameValues_successfulResponse() = runBlocking{ + //GIVEN + val currencyOne = "AUD - Australian Dollar" + val currencyTwo = "GBP - British Pound" + viewModel.rateIdTo = currencyOne + val tag = "top" + val responseObject = mock(ResponseObject::class.java) + + //WHEN + Mockito.`when`(repository.getData(currencyOne, currencyTwo)).thenReturn(responseObject) + + //THEN + viewModel.setCurrencyName(tag, currencyOne) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(true, it.first) + assertNull(it.second) + } + } + + @Test + fun setCurrencyName_invalidValues_unsuccessfulResponse() = runBlocking{ + //GIVEN + val currencyOne = "AUD - Australian Dollar" + val currencyTwo = "GBP - British Pound" + val tag = "top" + val responseObject = mock(ResponseObject::class.java) + + //WHEN + Mockito.`when`(repository.getData(currencyOne, currencyTwo)).thenReturn(responseObject) + + //THEN + viewModel.setCurrencyName(tag, currencyOne) + viewModel.operationStartedListener.observeOnce { + assertEquals(true, it) + } + viewModel.operationFinishedListener.observeOnce { + assertEquals(false, it.first) + assertNotNull(it.second) + } + } + + @Test + fun getConversion_validValue_successfulResponse() { + //GIVEN + val inputDouble = "2.0" + + //THEN + val returnVal = viewModel.getConversion(inputDouble) + assertEquals(returnVal, inputDouble) + } + + @Test + fun getConversion_invalidValue_unsuccessfulResponse() { + //THEN + val returnVal = viewModel.getConversion("t") + assertNull(returnVal) + } + + @Test + fun getReciprocalConversion_validValue_successfulResponse() { + //GIVEN + val inputDouble = "2.0" + + //THEN + val returnVal = viewModel.getReciprocalConversion(inputDouble) + assertEquals(returnVal, "2.0") + } + + @Test + fun getReciprocalConversion_invalidValue_unsuccessfulResponse() { + //THEN + val returnVal = viewModel.getReciprocalConversion("t") + assertNull(returnVal) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/appttude/h_mal/easycc/ui/widget/WidgetViewModelTest.kt b/app/src/test/java/com/appttude/h_mal/easycc/ui/widget/WidgetViewModelTest.kt new file mode 100644 index 0000000..cb2a155 --- /dev/null +++ b/app/src/test/java/com/appttude/h_mal/easycc/ui/widget/WidgetViewModelTest.kt @@ -0,0 +1,118 @@ +package com.appttude.h_mal.easycc.ui.widget + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.appttude.h_mal.easycc.data.repository.Repository +import com.appttude.h_mal.easycc.utils.observeOnce +import org.junit.Before +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations + +private const val currencyOne = "AUD - Australian Dollar" +private const val currencyTwo = "GBP - British Pound" +class WidgetViewModelTest { + + // Run tasks synchronously + @get:Rule + val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule() + + lateinit var viewModel: WidgetViewModel + + @Mock + lateinit var repository: Repository + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + viewModel = WidgetViewModel(repository) + } + + @Test + fun initiate_validInput_successfulResponse() { + //GIVEN + val appId = 123 + val pair = Pair(currencyOne,currencyTwo) + + //WHEN + Mockito.`when`(repository.getWidgetConversionPairs(appId)).thenReturn(pair) + + //THEN + viewModel.initiate(123) + assertEquals(viewModel.rateIdFrom, currencyOne) + assertEquals(viewModel.rateIdTo, currencyTwo) + } + + @Test + fun initiate_validInputNoWidgetPair_successfulResponse() { + //GIVEN + val appId = 123 + val pair = mock(Pair::class.java) + val array = arrayOf(currencyOne) + + //WHEN + Mockito.`when`(repository.getWidgetConversionPairs(appId)).thenAnswer { pair } + Mockito.`when`(repository.getWidgetConversionPairs(appId).first).thenReturn(null) + Mockito.`when`(repository.getWidgetConversionPairs(appId).second).thenReturn(null) + Mockito.`when`(repository.getArrayList()).thenReturn(array) + + //THEN + viewModel.initiate(123) + assertEquals(viewModel.rateIdFrom, currencyOne) + assertEquals(viewModel.rateIdTo, currencyOne) + } + + @Test + fun getSubmitDialogMessage_validInput_successfulResponse() { + //GIVEN + viewModel.rateIdFrom = currencyOne + viewModel.rateIdTo = currencyTwo + + //THEN + val dialogResult = viewModel.getSubmitDialogMessage() + assertEquals(dialogResult, "Create widget for AUDGBP?") + + } + + @Test + fun submitSelectionOnClick_validInput_successfulResponse() { + //GIVEN + viewModel.rateIdFrom = currencyOne + viewModel.rateIdTo = currencyTwo + + //THEN + viewModel.submitSelectionOnClick() + viewModel.operationFinishedListener.observeOnce { + assertEquals(it.first, true) + assertNull(it.second) + } + } + + @Test + fun submitSelectionOnClick_invalidInput_unsuccessfulResponse() { + //GIVEN + viewModel.rateIdFrom = currencyOne + viewModel.rateIdTo = currencyOne + + //THEN + viewModel.submitSelectionOnClick() + viewModel.operationFinishedListener.observeOnce { + assertEquals(it.first, false) + assertNotNull(it.second) + } + } + + @Test + fun submitSelectionOnClick_noInput_unsuccessfulResponse() { + + //THEN + viewModel.submitSelectionOnClick() + viewModel.operationFinishedListener.observeOnce { + assertEquals(it.first, false) + assertNotNull(it.second) + } + } +} \ No newline at end of file diff --git a/app/src/test/java/com/appttude/h_mal/easycc/utils/TestUtils.kt b/app/src/test/java/com/appttude/h_mal/easycc/utils/TestUtils.kt new file mode 100644 index 0000000..282c370 --- /dev/null +++ b/app/src/test/java/com/appttude/h_mal/easycc/utils/TestUtils.kt @@ -0,0 +1,9 @@ +package com.appttude.h_mal.easycc.utils + +import androidx.lifecycle.LiveData +import com.appttude.h_mal.easycc.OneTimeObserver + +fun LiveData.observeOnce(onChangeHandler: (T) -> Unit) { + val observer = OneTimeObserver(handler = onChangeHandler) + observe(observer, observer) +} \ No newline at end of file