From 63fe6bf634dcfdd94d5111391983f875d61b8ea4 Mon Sep 17 00:00:00 2001 From: arka Date: Mon, 23 Aug 2021 12:45:18 +0430 Subject: [PATCH 1/2] add test to statisticUtils --- app/build.gradle | 10 +++ .../todoapp/statistics/StatisticsUtils.kt | 17 ++-- .../blueprints/todoapp/ExampleUnitTest.kt | 5 ++ .../todoapp/statistics/StatisticsUtilsTest.kt | 81 +++++++++++++++++++ build.gradle | 2 +- 5 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 app/src/test/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtilsTest.kt diff --git a/app/build.gradle b/app/build.gradle index 32fd8d262..fbce1421e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,12 @@ android { enabled = true enabledForTests = true } + android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + } } dependencies { @@ -61,4 +67,8 @@ dependencies { // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" implementation "androidx.fragment:fragment-ktx:$fragmentKtxVersion" + + //other + testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion" + } diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtils.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtils.kt index 968b8f144..00533ca70 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtils.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtils.kt @@ -22,12 +22,17 @@ import com.example.android.architecture.blueprints.todoapp.data.Task * Function that does some trivial computation. Used to showcase unit tests. */ internal fun getActiveAndCompletedStats(tasks: List?): StatsResult { - val totalTasks = tasks!!.size - val numberOfActiveTasks = tasks.count { it.isActive } - return StatsResult( - activeTasksPercent = 100f * numberOfActiveTasks / tasks.size, - completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size - ) + return if (tasks.isNullOrEmpty()){ + StatsResult(0f,0f) + }else{ + val totalTasks = tasks.size + val numberOfActiveTasks = tasks.count { it.isActive } + StatsResult( + activeTasksPercent = 100f * numberOfActiveTasks / tasks.size, + completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size + ) + } + } data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float) diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt index 19ae73aeb..9ed672909 100644 --- a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt @@ -14,4 +14,9 @@ class ExampleUnitTest { fun addition_isCorrect() { assertEquals(4, 2 + 2) } + + @Test + fun multi_isCorrect(){ + assertEquals(6,2*2) + } } diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtilsTest.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtilsTest.kt new file mode 100644 index 000000000..8c099a520 --- /dev/null +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsUtilsTest.kt @@ -0,0 +1,81 @@ +package com.example.android.architecture.blueprints.todoapp.statistics + +import com.example.android.architecture.blueprints.todoapp.data.Task +import org.junit.Assert.* +import org.junit.Test + +class StatisticsUtilsTest{ + @Test + fun getActiveAndCompletedStats_empty_returnZeros(){ + // create active tasks + + //call your function + val result= getActiveAndCompletedStats(emptyList()) + + //check the result + assertEquals(result.activeTasksPercent,0f) + assertEquals(result.completedTasksPercent,0f) + } + + @Test + fun getActiveAndCompletedStats_null_returnZeros(){ + // create active tasks + + //call your function + val result= getActiveAndCompletedStats(null) + + //check the result + assertEquals(result.activeTasksPercent,0f) + assertEquals(result.completedTasksPercent,0f) + } + + @Test + fun getActiveAndCompletedStats_both_returnFiftyFifty(){ + // create active tasks + val tasks= listOf( + Task("title","description",true), + Task("title2","description2",false) + ) + + //call your function + val result= getActiveAndCompletedStats(tasks) + + //check the result + assertEquals(result.activeTasksPercent,50f) + assertEquals(result.completedTasksPercent,50f) + } + + @Test + fun getActiveAndCompletedStats_noComplete_returnZeroHundred(){ + // create active tasks + val tasks= listOf( + Task("title","description",true), + Task("title2","description2",true) + ) + + //call your function + val result= getActiveAndCompletedStats(tasks) + + //check the result + assertEquals(result.activeTasksPercent,0f) + assertEquals(result.completedTasksPercent,100f) + } + + @Test + fun getActiveAndCompletedStats_noActive_returnHundredZero(){ + // create active tasks + val tasks= listOf( + Task("title","description",false), + Task("title2","description2",false) + ) + + //call your function + val result= getActiveAndCompletedStats(tasks) + + //check the result + assertEquals(result.activeTasksPercent,100f) + assertEquals(result.completedTasksPercent,0f) + } + +} + diff --git a/build.gradle b/build.gradle index dfe1b541a..8a1cedf56 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" From 523cbaafb48f07f1097b9c8d3f498890667e8a0a Mon Sep 17 00:00:00 2001 From: arka Date: Tue, 24 Aug 2021 13:09:32 +0430 Subject: [PATCH 2/2] add test for tasksViewModel --- app/build.gradle | 13 +++++ .../blueprints/todoapp/ExampleUnitTest.kt | 3 +- .../blueprints/todoapp/LiveDataTestUtil.kt | 42 ++++++++++++++++ .../todoapp/tasks/TasksViewModelTest.kt | 48 +++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/src/test/java/com/example/android/architecture/blueprints/todoapp/LiveDataTestUtil.kt create mode 100644 app/src/test/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModelTest.kt diff --git a/app/build.gradle b/app/build.gradle index fbce1421e..dde878b93 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,10 @@ android { } } + testOptions.unitTests { + includeAndroidResources = true + } + dataBinding { enabled = true enabledForTests = true @@ -47,6 +51,7 @@ dependencies { // Architecture Components implementation "androidx.room:room-runtime:$roomVersion" + implementation 'androidx.test:core-ktx:1.4.0' kapt "androidx.room:room-compiler:$roomVersion" implementation "androidx.room:room-ktx:$roomVersion" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$archLifecycleVersion" @@ -64,6 +69,14 @@ dependencies { androidTestImplementation "androidx.test.ext:junit:$androidXTestExtKotlinRunnerVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" + // AndroidX Test - JVM testing + testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion" + testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion" + testImplementation "org.robolectric:robolectric:$robolectricVersion" + + testImplementation "androidx.arch.core:core-testing:$archTestingVersion" + + // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" implementation "androidx.fragment:fragment-ktx:$fragmentKtxVersion" diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt index 9ed672909..2a9ce357b 100644 --- a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/ExampleUnitTest.kt @@ -9,6 +9,7 @@ import org.junit.Assert.* * * See [testing documentation](http://d.android.com/tools/testing). */ + class ExampleUnitTest { @Test fun addition_isCorrect() { @@ -17,6 +18,6 @@ class ExampleUnitTest { @Test fun multi_isCorrect(){ - assertEquals(6,2*2) + assertEquals(6,2*3) } } diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/LiveDataTestUtil.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/LiveDataTestUtil.kt new file mode 100644 index 000000000..79525b756 --- /dev/null +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/LiveDataTestUtil.kt @@ -0,0 +1,42 @@ +package com.example.android.architecture.blueprints.todoapp + +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + + +@VisibleForTesting(otherwise = VisibleForTesting.NONE) +fun LiveData.getOrAwaitValue( + time: Long = 2, + timeUnit: TimeUnit = TimeUnit.SECONDS, + afterObserve: () -> Unit = {} +): T { + var data: T? = null + val latch = CountDownLatch(1) + val observer = object : Observer { + override fun onChanged(o: T?) { + data = o + latch.countDown() + this@getOrAwaitValue.removeObserver(this) + } + } + this.observeForever(observer) + + try { + afterObserve.invoke() + + // Don't wait indefinitely if the LiveData is not set. + if (!latch.await(time, timeUnit)) { + throw TimeoutException("LiveData value was never set.") + } + + } finally { + this.removeObserver(observer) + } + + @Suppress("UNCHECKED_CAST") + return data as T +} \ No newline at end of file diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModelTest.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModelTest.kt new file mode 100644 index 000000000..17f2f7e5c --- /dev/null +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModelTest.kt @@ -0,0 +1,48 @@ +package com.example.android.architecture.blueprints.todoapp.tasks + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue +import org.junit.Assert.* +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class TasksViewModelTest{ + // subject under test + private lateinit var taskViewModel: TasksViewModel + + @get:Rule + var instantExecutorRule = InstantTaskExecutorRule() + + @Before + fun setUpViewModel(){ + // Given a fresh ViewModel + taskViewModel = TasksViewModel(ApplicationProvider.getApplicationContext()) + } + + @Test + fun addNewTask_setsNewTaskEvent(){ + + //When adding a new task + taskViewModel.addNewTask() + + //then the new task event is triggered + val value= taskViewModel.newTaskEvent.getOrAwaitValue() + assertNotNull(value.getContentIfNotHandled()) + } + + @Test + fun setFilterAllTasks_tasksAddViewVisible() { + + // When the filter type is ALL_TASKS + taskViewModel.setFiltering(TasksFilterType.ALL_TASKS) + + // Then the "Add task" action is visible + assertEquals(taskViewModel.tasksAddViewVisible.getOrAwaitValue(),true) + + } +}