diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 16ed8b4..1c885b9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,7 +26,7 @@ android { targetSdk = (ProjectProperties.TARGET_SDK) versionCode = 1 versionName = "1.0" - testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner = "com.mutualmobile.base.TextPraxisApplication" vectorDrawables.useSupportLibrary = true } @@ -112,7 +112,12 @@ dependencies { testImplementation(TestLib.ROBO_ELECTRIC) testImplementation(TestLib.COROUTINES) testImplementation(TestLib.MOCKK) + androidTestImplementation(TestLib.MOCKK) + androidTestImplementation(TestLib.MOCK_WEB_SERVER) androidTestImplementation("androidx.compose.ui:ui-test-junit4:${Lib.Android.COMPOSE_VERSION}") debugImplementation("androidx.compose.ui:ui-test-manifest:${Lib.Android.COMPOSE_VERSION}") + androidTestImplementation("com.google.dagger:hilt-android-testing:2.38.1") + kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.38.1") + androidTestImplementation("androidx.test:runner:1.4.0") } diff --git a/app/src/androidTest/java/com/mutualmobile/base/BaseComposeTest.kt b/app/src/androidTest/java/com/mutualmobile/base/BaseComposeTest.kt new file mode 100644 index 0000000..ae621de --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/base/BaseComposeTest.kt @@ -0,0 +1,36 @@ +package com.mutualmobile.base + +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.mutualmobile.praxis.root.MainActivity +import dagger.hilt.android.testing.HiltAndroidRule +import javax.inject.Inject +import okhttp3.mockwebserver.MockWebServer +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +abstract class BaseComposeTest { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Inject + lateinit var mockWebServer: MockWebServer + + @Before + fun setup() { + hiltRule.inject() + } + + @After + fun tearDown() { + mockWebServer.shutdown() + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/mutualmobile/base/TestPraxisApplication.kt b/app/src/androidTest/java/com/mutualmobile/base/TestPraxisApplication.kt new file mode 100644 index 0000000..23acefb --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/base/TestPraxisApplication.kt @@ -0,0 +1,20 @@ +package com.mutualmobile.base + +import android.app.Application +import android.content.Context +import android.os.Bundle +import android.os.StrictMode +import androidx.test.runner.AndroidJUnitRunner +import dagger.hilt.android.testing.HiltTestApplication + +class TextPraxisApplication : AndroidJUnitRunner() { + + override fun onCreate(arguments: Bundle?) { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().permitAll().build()) + super.onCreate(arguments) + } + + override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { + return super.newApplication(cl, HiltTestApplication::class.java.name, context) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/mutualmobile/di/FakeNetworkModule.kt b/app/src/androidTest/java/com/mutualmobile/di/FakeNetworkModule.kt new file mode 100644 index 0000000..4b54b5b --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/di/FakeNetworkModule.kt @@ -0,0 +1,65 @@ +package com.mutualmobile.di + +import com.mutualmobile.praxis.data.injection.NetworkModule +import com.mutualmobile.praxis.data.remote.JokeApiService +import com.mutualmobile.praxis.data.remote.RetrofitHelper +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.hilt.testing.TestInstallIn +import javax.inject.Named +import javax.inject.Singleton +import okhttp3.OkHttpClient +import okhttp3.mockwebserver.MockWebServer +import retrofit2.Retrofit + +@Module +@TestInstallIn( + components = [SingletonComponent::class], + replaces = [NetworkModule::class] +) +class TestNetworkModule { + + @Provides + @Singleton + fun provideMockWebServer(): MockWebServer { + var mockWebServer: MockWebServer? = null + val thread = Thread { + mockWebServer = MockWebServer() + mockWebServer?.start() + } + thread.start() + thread.join() + return mockWebServer!! + } + + @Provides + @Singleton + fun provideBaseUrl(mockWebServer: MockWebServer): String { + return mockWebServer.url("/") + .toString() + } + + @Provides + @Singleton + fun provideHttpClient(): OkHttpClient { + return RetrofitHelper.createOkHttpClient() + } + + @Provides + @Singleton + fun provideRetrofit( + okHttpClient: OkHttpClient, + rootUrl: String + ): Retrofit { + return RetrofitHelper.createRetrofitClient(okHttpClient, rootUrl) + } + + @Provides + @Singleton + fun provideJokesApiService(retrofit: Retrofit): JokeApiService { + return JokeApiService.createRetrofitService(retrofit) + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/mutualmobile/feat/jokes/ui/home/DashboardShould.kt b/app/src/androidTest/java/com/mutualmobile/feat/jokes/ui/home/DashboardShould.kt new file mode 100644 index 0000000..d202457 --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/feat/jokes/ui/home/DashboardShould.kt @@ -0,0 +1,42 @@ +package com.mutualmobile.feat.jokes.ui.home + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import com.mutualmobile.base.BaseComposeTest +import com.mutualmobile.praxis.commonui.theme.PraxisTheme +import com.mutualmobile.utils.enqueueResponse +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Test + +@HiltAndroidTest +class DashboardShould : BaseComposeTest() { + + @Test + fun display_demo_jokes() { + mockWebServer.enqueueResponse("jokes_response.json", 200) + composeTestRule.setContent { + PraxisTheme { + Dashboard() + } + } + with(composeTestRule) { + onNodeWithText("Chuck Norris can believe it's not butter.").assertIsDisplayed() + } + } + + @Test + fun display_jokeDetailScreen_onClick_of_joke() { + with(composeTestRule) { + onNodeWithText("Email").performTextInput("shubham@gmail.com") + onNodeWithText("Password").performTextInput("testpassword") + onNodeWithText("Login").performClick() + onNodeWithText("Chuck Norris Random Joke Generator").assertIsDisplayed() + mockWebServer.enqueueResponse("jokes_response.json", 200) + onNodeWithText("Chuck Norris can believe it's not butter.").performClick() + onNodeWithText("Joke Detail").assertIsDisplayed() + } + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/mutualmobile/praxis/feat/authentication/ui/AuthenticationUIShould.kt b/app/src/androidTest/java/com/mutualmobile/praxis/feat/authentication/ui/AuthenticationUIShould.kt new file mode 100644 index 0000000..0672863 --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/praxis/feat/authentication/ui/AuthenticationUIShould.kt @@ -0,0 +1,40 @@ +package com.mutualmobile.praxis.feat.authentication.ui + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import com.mutualmobile.base.BaseComposeTest +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Test + +@HiltAndroidTest +class AuthenticationUIShould: BaseComposeTest() { + + @Test + fun display_emailErrorSnackBar_onClick_of_loginBtn_with_empty_credentials() { + with(composeTestRule) { + onNodeWithText("Login").performClick() + onNodeWithText("Email is not valid").assertIsDisplayed() + } + } + + @Test + fun display_passwordErrorSnackBar_onClick_of_loginBtn_with_empty_password_only() { + with(composeTestRule) { + onNodeWithText("Email").performTextInput("shubham@gmail.com") + onNodeWithText("Login").performClick() + onNodeWithText("Password should be at least 6 characters long").assertIsDisplayed() + } + } + + @Test + fun display_emailErrorSnackBar_onClick_of_loginBtn_with_empty_email_only() { + with(composeTestRule) { + onNodeWithText("Password").performTextInput("testpassword") + onNodeWithText("Login").performClick() + onNodeWithText("Email is not valid").assertIsDisplayed() + } + } + +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/mutualmobile/praxis/root/PraxisNavigationShould.kt b/app/src/androidTest/java/com/mutualmobile/praxis/root/PraxisNavigationShould.kt new file mode 100644 index 0000000..3f9c5cc --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/praxis/root/PraxisNavigationShould.kt @@ -0,0 +1,46 @@ +package com.mutualmobile.praxis.root + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import com.mutualmobile.base.BaseComposeTest +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Test + +@HiltAndroidTest +class PraxisNavigationShould : BaseComposeTest() { + + @Test + fun check_if_all_items_are_displayed_correctly() { + with(composeTestRule) { + onNodeWithText("Authentication").assertIsDisplayed() + onNodeWithContentDescription("Logo").assertIsDisplayed() + onNodeWithText("Email").assertIsDisplayed() + onNodeWithContentDescription("Email").assertIsDisplayed() + onNodeWithText("Password").assertIsDisplayed() + onNodeWithContentDescription("Password").assertIsDisplayed() + onNodeWithText("Login").assertIsDisplayed() + onNodeWithText("Forgot Password?").assertIsDisplayed() + } + } + + @Test + fun navigate_to_forgotPasswordScreen_onClick_of_forgotPassword_text() { + with(composeTestRule) { + onNodeWithText("Forgot Password?").performClick() + onNodeWithText("Forgot Password").assertIsDisplayed() + } + } + + @Test + fun navigate_to_dashboard_on_correct_credentials() { + with(composeTestRule) { + onNodeWithText("Email").performTextInput("shubham@gmail.com") + onNodeWithText("Password").performTextInput("testpassword") + onNodeWithText("Login").performClick() + onNodeWithText("Chuck Norris Random Joke Generator").assertIsDisplayed() + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/mutualmobile/utils/NetworkUtils.kt b/app/src/androidTest/java/com/mutualmobile/utils/NetworkUtils.kt new file mode 100644 index 0000000..d67fa11 --- /dev/null +++ b/app/src/androidTest/java/com/mutualmobile/utils/NetworkUtils.kt @@ -0,0 +1,20 @@ +package com.mutualmobile.utils + +import java.nio.charset.StandardCharsets +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okio.buffer +import okio.source + +internal fun MockWebServer.enqueueResponse(fileName: String, code: Int) { + val inputStream = javaClass.classLoader?.getResourceAsStream("responses/$fileName") + + val source = inputStream?.let { inputStream.source().buffer() } + source?.let { + enqueue( + MockResponse() + .setResponseCode(code) + .setBody(source.readString(StandardCharsets.UTF_8)) + ) + } +} \ No newline at end of file diff --git a/app/src/androidTest/resources/responses/jokes_response.json b/app/src/androidTest/resources/responses/jokes_response.json new file mode 100644 index 0000000..695c25c --- /dev/null +++ b/app/src/androidTest/resources/responses/jokes_response.json @@ -0,0 +1,30 @@ +{ + "type": "success", + "value": [ + { + "id": 427, + "joke": "Chuck Norris' favorite cereal is Kellogg's Nails 'N' Gravel.", + "categories": [] + }, + { + "id": 75, + "joke": "Chuck Norris can believe it's not butter.", + "categories": [] + }, + { + "id": 302, + "joke": "Chuck Norris doesn't go on the internet, he has every internet site stored in his memory. He refreshes webpages by blinking.", + "categories": [] + }, + { + "id": 275, + "joke": "Little Miss Muffet sat on her tuffet, until Chuck Norris roundhouse kicked her into a glacier.", + "categories": [] + }, + { + "id": 76, + "joke": "If tapped, a Chuck Norris roundhouse kick could power the country of Australia for 44 minutes.", + "categories": [] + } + ] +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c8f8a14..7ce7326 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,13 +6,15 @@ + tools:ignore="GoogleAppIndexingWarning" + tools:targetApi="m">