Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
Update dependencies version
  • Loading branch information
sridhar-sp committed Jan 3, 2025
1 parent 8f90b07 commit bbdae81
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 36 deletions.
212 changes: 204 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ etc., to achieve this isolation.
<details>
<summary>Simple test without mocks</summary>

```
// Dependency
// Regular JUnit dependency
testImplementation("junit:junit:4.13.2")
// Assertion library
testImplementation("com.google.truth:truth:1.1.4")
// Allows us to create and configure mock objects, stub methods, verify method invocations, and more
testImplementation("io.mockk:mockk:1.13.5")
```

### System under test

```kotlin
Expand Down Expand Up @@ -218,6 +231,35 @@ UI testing usually refers testing the user interface by simulating user action a
<details>
<summary>Compose UI+Interaction Unit Test </summary>

```
// Dependencies
// Allows us to create and configure mock objects, stub methods, verify method invocations, and more
androidTestImplementation("io.mockk:mockk-agent:1.13.5")
androidTestImplementation("io.mockk:mockk-android:1.13.5")
androidTestImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// Assertion library
androidTestImplementation("com.google.truth:truth:1.1.4")
// Needed for createComposeRule , createAndroidComposeRule and other rules used to perform UI test
testImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with robolectric to run ui test on jvm
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with AndroidTestRunner to run ui test on virtual/physical device.
// Needed for createComposeRule(), but not for createAndroidComposeRule<YourActivity>():
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
// Dependency injection for For instrumented tests on JVM
testImplementation("com.google.dagger:hilt-android-testing:2.49")
kaptTest("com.google.dagger:hilt-compiler:2.49")
// Needed to run android UI test on JVM instead of on an emulator or device
testImplementation("org.robolectric:robolectric:4.10.3)
// Helper for other arch dependencies, including JUnit test rules that can be used with LiveData, coroutines etc
testImplementation("androidx.arch.core:core-testing:2.2.0")
```

### System under test

Test uses `RobolectricTestRunner` to run code on `JVM`.
Expand Down Expand Up @@ -336,21 +378,77 @@ class LoginKtTest {

## Integration testing

Integration testing usually refers testing interaction between different components or modules of an application.
Integration testing typically involves testing the interactions between different components or modules of an
application.

During these tests, we can visually observe the app launching, with all the interactions specified in the code happening
in real time.

However, there’s an alternative approach that leverages GradleManagedDevices to run integration tests. This method skips
the UI preview and executes the tests on a configured virtual or physical device. More details on this approach are
provided in the next section.

### Integration Testing Frameworks

| Framework | Description |
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Robolectric | To perform android UI/functional testing on JVM without the need for android device.<br/> * Test files are located inside the test folder |
| AndroidX test runner | Provides AndroidJUnitRunner which is a JUnit test runner that allows to run instrumented JUnit 4 tests on Android devices, including those using the Espresso, UI Automator, and Compose testing frameworks. <br/> * Test files are located inside the androidTest folder. |
| UI Automator | |

Test uses `AndroidJUnitRunner` to run on android virtual/physical device.

```
// Used to create AndroidHiltTestRunner from AndroidJUnitRunner
androidTestImplementation("androidx.test:runner:1.6.2")
```

```kotlin
android {

defaultConfig {
// testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

// If we are using Hilt we can extend the AndroidJUnitRunner and pass the HiltTestApplication as application component.
testInstrumentationRunner = "com.gandiva.android.sample.AndroidHiltTestRunner"
}
}
```

```kotlin
class AndroidHiltTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
}
```

### Robolectric

<details>
<summary>Instrumentation test with Robolectric</summary>

Test uses `AndroidJUnitRunner` to run on android virtual/physical device.
You can observe from the test cases it look similar to the Compose UI Unit Test code snippet,
because `androidx.compose.ui.test.junit4` library has the test implementation for both the JVM and Android. so using the
same interfaces we can run the test on both runtime. Based on the test runner configured it will use the corresponding
implementation at runtime.

The `androidx.compose.ui.test.junit4` module includes a `ComposeTestRule` and an implementation for Android
called `AndroidComposeTestRule`. Through this rule you can set Compose content or access the activity. You construct the
rules using factory functions, either `createComposeRule` or, if you need access to an
activity, `createAndroidComposeRule`.

```
// Dependencies
// Needed for createComposeRule , createAndroidComposeRule and other rules used to perform UI test
testImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with robolectric to run ui test on jvm
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with AndroidTestRunner to run ui test on virtual/physical device.
// Required to add androidx.activity.ComponentActivity to test manifest.
// Needed for createComposeRule(), but not for createAndroidComposeRule<YourActivity>():
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
```

### System under test

Expand Down Expand Up @@ -444,19 +542,70 @@ class LoginKtTest {

</details>

### Android JUnit test

*** Write few lines about Android JUnit test ***

```
// To perform UI automation test.
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
```

<details>
<summary>Instrumentation test that runs on Virtual/Physical/GradleManagedDevice</summary>

### System under test

Explain about system under test

### Test

```kotlin

```

</details>

### UI Automator

*** Write few lines about UI Automator ***

```
// To perform UI automation test.
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
```

<details>
<summary>Instrumentation test with UI Automator</summary>

### System under test

Explain about system under test

### Test

```kotlin

```
</details>

### Command

```shell
./gradlew connectedAndroidTest --continue
```

### Integration Testing Support

### Gradle Managed Devices

Gradle Managed Devices offers a way to configure a virtual or real device in Gradle to run the integration test. Since
the configuration is added to Gradle, it allows Gradle to be aware of the device lifecycle and can start or shut down
the device as required.
Gradle Managed Devices provide a way to configure virtual or physical devices directly in Gradle for running integration
tests. Since the configuration is managed within Gradle, it gains full control over the device lifecycle, allowing it to
start or shut down devices as needed.

Unlike standard Android Virtual Devices (AVDs) or physical devices, there won’t be any visual preview during the test
run. Once the test completes, you can review the results in the reports generated in the build folder.

Gradle Managed Devices are primarily used for running automated tests at scale on various virtual devices, so the focus
is on configuration details rather than a visual representation.

### Setup

Expand All @@ -469,7 +618,6 @@ testOptions {
apiLevel = 34
systemImageSource = "aosp"
}

}
}
}
Expand All @@ -492,6 +640,7 @@ testOptions {
* https://martinfowler.com/articles/practical-test-pyramid.html#ProviderTestourTeam
* https://martinfowler.com/bliki/TestDouble.html
* https://developer.android.com/studio/test/gradle-managed-devices
* https://developer.android.com/training/testing/other-components/ui-automator

<hr/>

Expand All @@ -516,5 +665,52 @@ Points
- DD-style way of writing tests
- Talking about different test classifications is always difficult.

```
// Dependencies
// Allows us to create and configure mock objects, stub methods, verify method invocations, and more
androidTestImplementation("io.mockk:mockk-agent:1.13.5")
androidTestImplementation("io.mockk:mockk-android:1.13.5")
androidTestImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
// Assertion library
androidTestImplementation("com.google.truth:truth:1.1.4")
// Needed for createComposeRule , createAndroidComposeRule and other rules used to perform UI test
testImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with robolectric to run ui test on jvm
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") // used with AndroidTestRunner to run ui test on virtual/physical device.
// Required to add androidx.activity.ComponentActivity to test manifest.
// Needed for createComposeRule(), but not for createAndroidComposeRule<YourActivity>():
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
// To perform UI automation test.
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
// Used to create AndroidHiltTestRunner from AndroidJUnitRunner
androidTestImplementation("androidx.test:runner:1.6.2")
// Dependency injection for For instrumented tests on Android
androidTestImplementation("com.google.dagger:hilt-android-testing:2.49")
kaptAndroidTest("com.google.dagger:hilt-compiler:2.49")
// Dependency injection for For instrumented tests on JVM
testImplementation("com.google.dagger:hilt-android-testing:2.49")
kaptTest("com.google.dagger:hilt-compiler:2.49")
// Needed to run android UI test on JVM instead of on an emulator or device
testImplementation("org.robolectric:robolectric:4.10.3)
// Helper for other arch dependencies, including JUnit test rules that can be used with LiveData, coroutines etc
testImplementation("androidx.arch.core:core-testing:2.2.0")
// Regular JUnit dependency
testImplementation("junit:junit:4.13.2")
// Assertion library
testImplementation("com.google.truth:truth:1.1.4")
// Allows us to create and configure mock objects, stub methods, verify method invocations, and more
testImplementation("io.mockk:mockk:1.13.5")
```

16 changes: 9 additions & 7 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ dependencies {

debugImplementation(libs.androidx.compose.ui.tooling)

// Required to add androidx.activity.ComponentActivity to test manifest
// Required to add androidx.activity.ComponentActivity to test manifest.
// Needed for createComposeRule(), but not for createAndroidComposeRule<YourActivity>())
debugImplementation(libs.androidx.compose.ui.test.manifest)
// Used to create AndroidHiltTestRunner from AndroidJUnitRunner
androidTestImplementation(libs.androidx.test.runner)
Expand All @@ -106,26 +107,27 @@ dependencies {
androidTestImplementation(libs.mockk.agent)
androidTestImplementation(libs.mockk.android)
androidTestImplementation(libs.mockito.kotlin)

// Assertion library
androidTestImplementation(libs.truth)

// To perform UI automation test.
androidTestImplementation(libs.androidx.test.uiautomator)

// Regular JUnit dependency
testImplementation(libs.junit4)/* Needed for createAndroidComposeRule and other rules used to perform UI test - here we use robolectric to run ui
test on jvm */
testImplementation(libs.junit4)
// Needed for createAndroidComposeRule and other rules used to perform UI test - here we use robolectric to run ui test on jvm
testImplementation(libs.androidx.compose.ui.test.junit4)
// Needed to run android UI test on JVM instead of on an emulator or device
testImplementation(libs.robolectric)
// Allows us to create and configure mock objects, stub methods, verify method invocations, and more
testImplementation(libs.mockk)
// For testing coroutines
// Helper for other arch dependencies, including JUnit test rules that can be used with LiveData, coroutines etc
testImplementation(libs.androidx.core.testing)

// Assertion library
testImplementation(libs.truth)
// Dependency injection for For instrumented tests
testImplementation(libs.dagger.hilt.android.testing)
kaptTest(libs.dagger.hilt.compiler)

// To perform UI automation test.
androidTestImplementation(libs.androidx.test.uiautomator)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector

fun UiDevice.enterTextOnFieldWithId(resourceId: String, text: String) {
this.findObject(UiSelector().resourceId(resourceId)).text = text
this.findObject(UiSelector().resourceId(resourceId)).setText(text)
}

fun UiDevice.clickFieldWithId(resourceId: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.gandiva.android.sample.instrumentation
import android.content.Context
import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
Expand All @@ -16,7 +15,6 @@ import org.hamcrest.MatcherAssert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

private const val BASIC_SAMPLE_PACKAGE = "com.gandiva.android.sample"
private const val LAUNCH_TIMEOUT = 5000L
Expand Down Expand Up @@ -46,26 +44,16 @@ class LoginJourneyTest {
// Wait for launcher
val launcherPackage: String = device.launcherPackageName
MatcherAssert.assertThat(launcherPackage, CoreMatchers.notNullValue())
device.wait(
Until.hasObject(By.pkg(launcherPackage).depth(0)),
LAUNCH_TIMEOUT
)
device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT)

// Launch the app
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager.getLaunchIntentForPackage(
BASIC_SAMPLE_PACKAGE
)?.apply {
// Clear out any previous instances
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
val intent = context.packageManager.getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE)
?.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) }
context.startActivity(intent)

// Wait for the app to appear
device.wait(
Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
LAUNCH_TIMEOUT
)
device.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.junit.Rule
import org.junit.Test

//@HiltAndroidTest
// Make sure the testInstrumentationRunner is either as androidx.test.runner.AndroidJUnitRunner or class extends it in build.gradle
class LoginKtTest {

@get:Rule
Expand Down
Loading

0 comments on commit bbdae81

Please sign in to comment.