diff --git a/app/build.gradle b/app/build.gradle index 6225f68..909f9d1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,14 @@ dependencies { testImplementation 'org.mockito:mockito-core:2.28.2' //Espresso androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test:core:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + debugImplementation "androidx.fragment:fragment-testing:$fragment_version" + androidTestImplementation "androidx.test.espresso:espresso-contrib:3.3.0" + implementation "androidx.test.espresso:espresso-idling-resource:3.3.0" + + //Components from androidx.arch for unit tests implementation 'com.google.code.gson:gson:2.8.6' //Mockito Kotlin testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" diff --git a/app/src/androidTest/java/com/noumanch/decadeofmovies/ui/activities/MainActivityUITest.kt b/app/src/androidTest/java/com/noumanch/decadeofmovies/ui/activities/MainActivityUITest.kt new file mode 100644 index 0000000..66e650a --- /dev/null +++ b/app/src/androidTest/java/com/noumanch/decadeofmovies/ui/activities/MainActivityUITest.kt @@ -0,0 +1,21 @@ +package com.noumanch.decadeofmovies.ui.activities + +import androidx.test.core.app.ActivityScenario +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import org.junit.Test +import org.junit.runner.RunWith +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import com.noumanch.decadeofmovies.R + +@RunWith(AndroidJUnit4ClassRunner::class) +class MainActivityUITest{ + + @Test + fun test_isActivityInView(){ + val activityScenario = ActivityScenario.launch(MainActivity::class.java) + onView(withId(R.id.main)).check(matches(isDisplayed())) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragmentTest.kt b/app/src/androidTest/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragmentTest.kt new file mode 100644 index 0000000..93fe30c --- /dev/null +++ b/app/src/androidTest/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragmentTest.kt @@ -0,0 +1,60 @@ +package com.noumanch.decadeofmovies.ui.fragments + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import com.noumanch.decadeofmovies.R +import com.noumanch.decadeofmovies.ui.activities.MainActivity +import com.noumanch.decadeofmovies.utils.FakeMovieData +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + + +@RunWith(AndroidJUnit4ClassRunner::class) +class MoviesFragmentTest { + + @get:Rule + val activityRule = ActivityScenarioRule(MainActivity::class.java) + + val LIST_ITEM_IN_TEST = 9 + val MOVIE_IN_TEST = FakeMovieData.movies[LIST_ITEM_IN_TEST] + + + @Test + fun test_isListFragmentVisible_onAppLaunch() { + onView(withId(R.id.recyclerView)).check(matches(isDisplayed())) + } + + @Test + fun test_selectListItem_isDetailFragmentVisible() { + // Click list item #LIST_ITEM_IN_TEST + onView(withId(R.id.recyclerView)) + .perform(actionOnItemAtPosition(LIST_ITEM_IN_TEST, click())) + + // Confirm nav to MovieDetailFragment and display title + onView(withId(R.id.movieTitleTV)).check(matches(withText(MOVIE_IN_TEST.title))) + } + + @Test + fun test_backNavigation_toMovieListFragment() { + // Click list item #LIST_ITEM_IN_TEST + onView(withId(R.id.recyclerView)) + .perform(actionOnItemAtPosition(LIST_ITEM_IN_TEST, click())) + + // Confirm nav to MovieDetailFragment and display title + onView(withId(R.id.movieTitleTV)).check(matches(withText(MOVIE_IN_TEST.title))) + + pressBack() + + // Confirm MoviesFragment in view + onView(withId(R.id.recyclerView)).check(matches(isDisplayed())) + } +} + + diff --git a/app/src/androidTest/java/com/noumanch/decadeofmovies/utils/FakeMovieData.kt b/app/src/androidTest/java/com/noumanch/decadeofmovies/utils/FakeMovieData.kt new file mode 100644 index 0000000..3b4841f --- /dev/null +++ b/app/src/androidTest/java/com/noumanch/decadeofmovies/utils/FakeMovieData.kt @@ -0,0 +1,99 @@ +package com.noumanch.decadeofmovies.utils + +import com.noumanch.decadeofmovies.models.Movie + +object FakeMovieData { + + val movies = arrayOf( + Movie( + 0, + "Avengers: Infinity War", + 2012,2, + arrayListOf("Anthony Russo", "Joe Russo"), + arrayListOf("Robert Downey Jr.", "Chris Hemsworth", "Mark Ruffalo", "+ more...") + ), + Movie( + 1, + "The Rundown", + 2012,2, + arrayListOf("R.J. Stewart", "James Vanderbilt"), + arrayListOf("Dwayne Johnson", "Seann William Scott", "Rosario Dawson", "Christopher Walken") + ), + Movie( + 2, + "The Godfather", + 2012,2, + arrayListOf("Francis Ford Coppola"), + arrayListOf("Marlon Brando", "Al Pacino", "James Caan") + ), + Movie( + 3, + "The Dark Knight", + 2012,2, + arrayListOf("Christopher Nolan"), + arrayListOf("Christian Bale", "Heath Ledger", "Aaron Eckhart") + ), + Movie( + 4, + "The Lord of the Rings: The Return of the King", + 2012,2, + arrayListOf("Peter Jackson"), + arrayListOf("Elijah Wood", "Viggo Mortensen", "Ian McKellen") + ), + Movie( + 3, + "The Dark Knight", + 2012,2, + arrayListOf("Christopher Nolan"), + arrayListOf("Christian Bale", "Heath Ledger", "Aaron Eckhart") + ), + Movie( + 4, + "The Lord of the Rings: The Return of the King", + 2012,2, + arrayListOf("Peter Jackson"), + arrayListOf("Elijah Wood", "Viggo Mortensen", "Ian McKellen") + ), + Movie( + 5, + "The Dark Knight", + 2012,2, + arrayListOf("Christopher Nolan"), + arrayListOf("Christian Bale", "Heath Ledger", "Aaron Eckhart") + ), + Movie( + 6, + "The Lord of the Rings: The Return of the King", + 2012,2, + arrayListOf("Peter Jackson"), + arrayListOf("Elijah Wood", "Viggo Mortensen", "Ian McKellen") + ), + Movie( + 7, + "The Dark Knight", + 2012,2, + arrayListOf("Christopher Nolan"), + arrayListOf("Christian Bale", "Heath Ledger", "Aaron Eckhart") + ), + Movie( + 8, + "The Lord of the Rings: The Return of the King", + 2012,2, + arrayListOf("Peter Jackson"), + arrayListOf("Elijah Wood", "Viggo Mortensen", "Ian McKellen") + ), Movie( + 9, + "The Dark Knight", + 2012,2, + arrayListOf("Christopher Nolan"), + arrayListOf("Christian Bale", "Heath Ledger", "Aaron Eckhart") + ), + Movie( + 10, + "The Lord of the Rings: The Return of the King", + 2012,2, + arrayListOf("Peter Jackson"), + arrayListOf("Elijah Wood", "Viggo Mortensen", "Ian McKellen") + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragment.kt b/app/src/main/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragment.kt index 0ca7c5c..1cdfe6f 100644 --- a/app/src/main/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragment.kt +++ b/app/src/main/java/com/noumanch/decadeofmovies/ui/fragments/MoviesFragment.kt @@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import com.noumanch.decadeofmovies.R import com.noumanch.decadeofmovies.databinding.FragmentMoviesBinding +import com.noumanch.decadeofmovies.utils.EspressoIdlingResource import com.noumanch.decadeofmovies.utils.extensions.hide import com.noumanch.decadeofmovies.utils.extensions.show import com.noumanch.decadeofmovies.utils.showAlertDialog @@ -56,6 +57,7 @@ class MoviesFragment : Fragment() { MoviesViewModel.GetMoviesViewState.Loading -> binding.progressBar.show() is MoviesViewModel.GetMoviesViewState.Success -> { binding.progressBar.hide() + EspressoIdlingResource.decrement() if (viewState.movies.size > 0) { binding.apply { recyclerView.show() @@ -110,6 +112,7 @@ class MoviesFragment : Fragment() { } } //load data + EspressoIdlingResource.increment() moviesViewModel.getMovies() } diff --git a/app/src/main/java/com/noumanch/decadeofmovies/utils/EspressoIdlingResource.kt b/app/src/main/java/com/noumanch/decadeofmovies/utils/EspressoIdlingResource.kt new file mode 100644 index 0000000..5328588 --- /dev/null +++ b/app/src/main/java/com/noumanch/decadeofmovies/utils/EspressoIdlingResource.kt @@ -0,0 +1,20 @@ +package com.noumanch.decadeofmovies.utils + +import androidx.test.espresso.idling.CountingIdlingResource + +object EspressoIdlingResource { + + private const val RESOURCE = "GLOBAL" + + @JvmField val countingIdlingResource = CountingIdlingResource(RESOURCE) + + fun increment() { + countingIdlingResource.increment() + } + + fun decrement() { + if (!countingIdlingResource.isIdleNow) { + countingIdlingResource.decrement() + } + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 7d1adf3..dd3ecd0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,6 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" + android:id="@+id/main" android:background="?android:attr/colorBackground" > diff --git a/assets/screenshot_2.png b/assets/screenshot_2.png index 053dab9..53e8b09 100644 Binary files a/assets/screenshot_2.png and b/assets/screenshot_2.png differ diff --git a/build.gradle b/build.gradle index 3025c31..973d002 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ buildscript { ext.navigationVersion = "2.3.3" ext.retrofit_version = "2.9.0" ext.room_version = "2.3.0" + ext.fragment_version = "1.3.4" repositories { google()