mirror of
https://github.com/android/skills.git
synced 2026-09-14 20:07:09 +08:00
Updates skills (2026-08-07 14:24)
This commit is contained in:
@@ -6,7 +6,7 @@ description: Analyze and create a testing strategy for native Android apps - ins
|
||||
license: Complete terms in LICENSE.txt
|
||||
metadata:
|
||||
author: Google LLC
|
||||
last-updated: '2026-06-25'
|
||||
last-updated: '2026-08-06'
|
||||
keywords:
|
||||
- android
|
||||
- testing
|
||||
@@ -17,8 +17,7 @@ metadata:
|
||||
|
||||
## Step 1: analyze the current testing setup
|
||||
|
||||
To understand the testing setup of an existing project, look for these
|
||||
dependencies in the libs.versions.toml file, or build files:
|
||||
To understand the testing setup of an existing project, look for these dependencies in the libs.versions.toml file, or build files:
|
||||
|
||||
1. Dependency Injection framework used. Examples: Hilt, Koin, Anvil, vanilla Dagger...
|
||||
2. Unit (local) testing framework this project uses, Example JUnit4, JUnit5...
|
||||
@@ -40,35 +39,26 @@ dependencies in the libs.versions.toml file, or build files:
|
||||
|
||||
## Step 2: Set up Dependency Injection frameworks for testing
|
||||
|
||||
If there is no Dependency Injection framework, install one: if it's a
|
||||
multiplatform application, ask the user whether they want to install Koin, or
|
||||
kotlin-inject. If it's not multiplatform, install Hilt.
|
||||
If there is no Dependency Injection framework, install one: if it's a multiplatform application, ask the user whether they want to install Koin, or kotlin-inject. If it's not multiplatform, install Hilt.
|
||||
|
||||
Install the testing dependencies (for example `com.google.dagger:hilt-compiler`
|
||||
that should be applied with a `kspAndroidTest` configuration).
|
||||
Install the testing dependencies (for example `com.google.dagger:hilt-compiler` that should be applied with a `kspAndroidTest` configuration).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Important:** Always consult the documentation of the applicable framework to learn about testing (for example: [Hilt testing guide](references/android/training/dependency-injection/hilt-testing.md), [Koin Instrumented tests](https://insert-koin.io/docs/reference/koin-android/instrumented-testing)).
|
||||
|
||||
For instrumented tests, create and configure (by adding
|
||||
`testInstrumentationRunner` to the build gradle files) a new test runner and
|
||||
apply the testing rules required by the framework (for example in Hilt, annotate
|
||||
your test classes with `@HiltAndroidTest` and apply the `HiltAndroidRule`).
|
||||
Other frameworks use other mechanisms, consult their documentation.
|
||||
For instrumented tests, create and configure (by adding `testInstrumentationRunner` to the build gradle files) a new test runner and apply the testing rules required by the framework (for example in Hilt, annotate your test classes with `@HiltAndroidTest` and apply the `HiltAndroidRule`). Other frameworks use other mechanisms, consult their documentation.
|
||||
|
||||
## Step 3: Install frameworks
|
||||
|
||||
Unless otherwise specified, respect the current stack of testing frameworks.
|
||||
|
||||
If there are no testing frameworks, and the user didn't specify any preference,
|
||||
install the following:
|
||||
If there are no testing frameworks, and the user didn't specify any preference, install the following:
|
||||
|
||||
- JUnit4 for local and instrumented tests
|
||||
- Jacoco for test coverage
|
||||
- For UI tests: if the project has views, Espresso. If it's fully Compose, use the Compose Testing APIs.
|
||||
- Robolectric to run UI Tests
|
||||
- Compose Preview Screenshot Testing tool for screenshot tests - check [setup
|
||||
documentation](references/android/studio/preview/compose-screenshot-testing.md) and follow it strictly.
|
||||
- Compose Preview Screenshot Testing tool for screenshot tests - check [setup documentation](references/android/studio/preview/compose-screenshot-testing.md) and follow it strictly.
|
||||
- Dropshots for device screenshot tests
|
||||
- If a mocking framework is necessary, install Mockk (`io.mockk:mockk`). Do not install it unless it is clearly necessary.
|
||||
|
||||
@@ -80,43 +70,30 @@ If end-to-end testing is requested, install UI Automator.
|
||||
|
||||
### **Refactor for unit tests**
|
||||
|
||||
In the next sections you'll be asked to create tests. If you have dependencies
|
||||
on Android framework classes, or entities that are not part of the codebase:
|
||||
In the next sections you'll be asked to create tests. If you have dependencies on Android framework classes, or entities that are not part of the codebase:
|
||||
|
||||
- First, use a fake. If it doesn't exist, create an interface for the class
|
||||
and a "Default" implementation with the existing code. Add the Fake version
|
||||
to the test sourceset (test or androidTest).
|
||||
- First, use a fake. If it doesn't exist, create an interface for the class and a "Default" implementation with the existing code. Add the Fake version to the test sourceset (test or androidTest).
|
||||
|
||||
- If not possible to use a fake (example: no access to the class or
|
||||
interface), mock the dependencies.
|
||||
- If not possible to use a fake (example: no access to the class or interface), mock the dependencies.
|
||||
|
||||
### **Refactor for UI tests**
|
||||
|
||||
If you need to fake components to make testing easier and faster and more
|
||||
reliable, replace slow and problematic dependencies with fakes. Use runtime
|
||||
fakes using the Dependency Injection framework installed to:
|
||||
If you need to fake components to make testing easier and faster and more reliable, replace slow and problematic dependencies with fakes. Use runtime fakes using the Dependency Injection framework installed to:
|
||||
|
||||
- **Simulate** different scenarios with the user (wrong credentials, reset password flow...), with a server (no connection, server down, bad JSON from server...) or with a platform component (insufficient permissions, no disk space, no front camera available)
|
||||
- **Improve** speed and reliability (replacing a database with an in-memory database, replacing a repository with an in-memory fake to avoid hitting the network)
|
||||
|
||||
## Step 5: Unit testing
|
||||
|
||||
Create a task to add or review unit tests in every file that contains business
|
||||
logic (ViewModels, Repositories, database-related classes such as DAOs, etc.).
|
||||
Don't create unit tests for Activities, Compose layouts, or dependency injection
|
||||
configuration files.
|
||||
Create a task to add or review unit tests in every file that contains business logic (ViewModels, Repositories, database-related classes such as DAOs, etc.). Don't create unit tests for Activities, Compose layouts, or dependency injection configuration files.
|
||||
|
||||
## Step 6: UI testing
|
||||
|
||||
Espresso or Compose UI tests live in the `test` sourceset because they will be
|
||||
run with Robolectric. If instrumented (emulator or device) tests are requested,
|
||||
put them in the `androidTest` sourceset.
|
||||
Espresso or Compose UI tests live in the `test` sourceset because they will be run with Robolectric. If instrumented (emulator or device) tests are requested, put them in the `androidTest` sourceset.
|
||||
|
||||
## Step 7: Test databases
|
||||
|
||||
If the database is using SQLite (using Room, SQLDelight, etc.), create
|
||||
instrumented tests using an in-memory database to make sure that they work with
|
||||
the SQLite engine on device.
|
||||
If the database is using SQLite (using Room, SQLDelight, etc.), create instrumented tests using an in-memory database to make sure that they work with the SQLite engine on device.
|
||||
|
||||
## Step 8: Screenshot tests
|
||||
|
||||
@@ -128,14 +105,11 @@ Irrespective of the framework used, screenshot tests focus on 2 types of tests:
|
||||
- Font scale set to 1.5.
|
||||
- Component-level screenshot tests, where each component is tested in different themes and font scales.
|
||||
|
||||
Behavior isn't tested with screenshots, but do test different common scenarios
|
||||
if their UIs change a lot depending on the state. For example, test loading
|
||||
screens by injecting a loading state to the UI or simulating it with a fake.
|
||||
Behavior isn't tested with screenshots, but do test different common scenarios if their UIs change a lot depending on the state. For example, test loading screens by injecting a loading state to the UI or simulating it with a fake.
|
||||
|
||||
## Step 9: UI Behavior tests
|
||||
|
||||
Test the UI logic using behavior tests, which ensures that the UIs react as
|
||||
expected when different states are passed, and when user actions are performed.
|
||||
Test the UI logic using behavior tests, which ensures that the UIs react as expected when different states are passed, and when user actions are performed.
|
||||
|
||||
### **Compose UI behavior tests**
|
||||
|
||||
@@ -157,25 +131,17 @@ Create a test suite to verify navigation logic. Include:
|
||||
|
||||
## Step 11: Simulate different window sizes and settings
|
||||
|
||||
For Compose layouts, use `DeviceConfigurationOverride` described in "[UI testing
|
||||
common patterns](references/android/develop/ui/compose/testing/common-patterns.md)" to simulate different window sizes, font scales
|
||||
For Compose layouts, use `DeviceConfigurationOverride` described in "[UI testing common patterns](references/android/develop/ui/compose/testing/common-patterns.md)" to simulate different window sizes, font scales
|
||||
|
||||
## Step 12: End-to-end tests
|
||||
|
||||
Create a low number (about 5% of all tests) of end-to-end tests that cover big
|
||||
user journeys. Use Compose Test APIs or Espresso for that. If you have to access
|
||||
platform features (notifications, system UI...), use UI Automator.
|
||||
Create a low number (about 5% of all tests) of end-to-end tests that cover big user journeys. Use Compose Test APIs or Espresso for that. If you have to access platform features (notifications, system UI...), use UI Automator.
|
||||
|
||||
If you need to take screenshots of the app running in a device, use
|
||||
[Dropshots](https://raw.githubusercontent.com/dropbox/dropshots/refs/heads/main/README.md). You need a device for screenshot tests when verifying
|
||||
interaction with the system UI (examples: edge-to-edge rendering, notifications,
|
||||
picture-in-picture)
|
||||
If you need to take screenshots of the app running in a device, use [Dropshots](https://raw.githubusercontent.com/dropbox/dropshots/refs/heads/main/README.md). You need a device for screenshot tests when verifying interaction with the system UI (examples: edge-to-edge rendering, notifications, picture-in-picture)
|
||||
|
||||
### Step 13: Instrumented Screenshot tests
|
||||
|
||||
Install the `com.dropbox.dropshots` plugin in the module and a `Dropshots()`
|
||||
JUnit Rule. Create a new instrumented screenshot test for one of the app's
|
||||
features.
|
||||
Install the `com.dropbox.dropshots` plugin in the module and a `Dropshots()` JUnit Rule. Create a new instrumented screenshot test for one of the app's features.
|
||||
|
||||
### Step 14: Install jacoco
|
||||
|
||||
@@ -185,14 +151,8 @@ Install jacoco for local testing code coverage.
|
||||
|
||||
## Final touches
|
||||
|
||||
- Ask whether to document the findings of the analysis and the changes applied
|
||||
to the testing strategy. If the user agrees:
|
||||
- Ask whether to document the findings of the analysis and the changes applied to the testing strategy. If the user agrees:
|
||||
|
||||
- If there is an AGENTS.md file present in the project, update it with any
|
||||
changes you've made to the testing strategy.
|
||||
- If there is an AGENTS.md file present in the project, update it with any changes you've made to the testing strategy.
|
||||
|
||||
- If there is no AGENTS.md file, create a new file (docs/testing.md) with
|
||||
a description of the testing strategy, including the commands needed to
|
||||
run every type of test, where the screenshot reference files live, etc.
|
||||
Also create a new AGENTS.md file in the root and create a link to
|
||||
docs/testing.md.
|
||||
- If there is no AGENTS.md file, create a new file (docs/testing.md) with a description of the testing strategy, including the commands needed to run every type of test, where the screenshot reference files live, etc. Also create a new AGENTS.md file in the root and create a link to docs/testing.md.
|
||||
|
||||
+21
-51
@@ -4,24 +4,15 @@ You can test your Compose app with well-established approaches and patterns.
|
||||
|
||||
### Test in isolation
|
||||
|
||||
[`ComposeTestRule`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule) lets you start an activity displaying any composable:
|
||||
your full application, a single screen, or a small element. It's also a good
|
||||
practice to check that your composables are correctly encapsulated and they work
|
||||
independently, allowing for easier and more focused UI testing.
|
||||
[`ComposeTestRule`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/junit4/ComposeTestRule) lets you start an activity displaying any composable: your full application, a single screen, or a small element. It's also a good practice to check that your composables are correctly encapsulated and they work independently, allowing for easier and more focused UI testing.
|
||||
|
||||
This doesn't mean you should *only* create unit UI tests. UI tests scoping
|
||||
larger parts of your UI are also very important.
|
||||
This doesn't mean you should *only* create unit UI tests. UI tests scoping larger parts of your UI are also very important.
|
||||
|
||||
### Access the activity and resources after setting your own content
|
||||
|
||||
Oftentimes you need to set the content under test using
|
||||
`composeTestRule.setContent` and you also need to access activity resources, for
|
||||
example to assert that a displayed text matches a string resource. However, you
|
||||
can't call `setContent` on a rule created with `createAndroidComposeRule()` if
|
||||
the activity already calls it.
|
||||
Oftentimes you need to set the content under test using `composeTestRule.setContent` and you also need to access activity resources, for example to assert that a displayed text matches a string resource. However, you can't call `setContent` on a rule created with `createAndroidComposeRule()` if the activity already calls it.
|
||||
|
||||
A common pattern to achieve this is to create an `AndroidComposeTestRule` using
|
||||
an empty activity such as [`ComponentActivity`](https://developer.android.com/reference/androidx/activity/ComponentActivity).
|
||||
A common pattern to achieve this is to create an `AndroidComposeTestRule` using an empty activity such as [`ComponentActivity`](https://developer.android.com/reference/androidx/activity/ComponentActivity).
|
||||
|
||||
class MyComposeTest {
|
||||
|
||||
@@ -41,17 +32,13 @@ an empty activity such as [`ComponentActivity`](https://developer.android.com/re
|
||||
}
|
||||
}
|
||||
|
||||
Note that `ComponentActivity` needs to be added to your app's
|
||||
`AndroidManifest.xml` file. Enable that by adding this dependency to your
|
||||
module:
|
||||
Note that `ComponentActivity` needs to be added to your app's `AndroidManifest.xml` file. Enable that by adding this dependency to your module:
|
||||
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
|
||||
|
||||
### Custom semantics properties
|
||||
|
||||
You can create custom [semantics](https://developer.android.com/develop/ui/compose/testing/semantics) properties to expose information to tests.
|
||||
To do this, define a new `SemanticsPropertyKey` and make it available using the
|
||||
`SemanticsPropertyReceiver`.
|
||||
You can create custom [semantics](https://developer.android.com/develop/ui/compose/testing/semantics) properties to expose information to tests. To do this, define a new `SemanticsPropertyKey` and make it available using the `SemanticsPropertyReceiver`.
|
||||
|
||||
// Creates a semantics property of type Long.
|
||||
val PickedDateKey = SemanticsPropertyKey<Long>("PickedDate")
|
||||
@@ -64,8 +51,7 @@ Now use that property in the `semantics` modifier:
|
||||
modifier = Modifier.semantics { pickedDate = datePickerValue }
|
||||
)
|
||||
|
||||
From tests, use `SemanticsMatcher.expectValue` to assert the value of the
|
||||
property:
|
||||
From tests, use `SemanticsMatcher.expectValue` to assert the value of the property:
|
||||
|
||||
composeTestRule
|
||||
.onNode(SemanticsMatcher.expectValue(PickedDateKey, 1445378400)) // 2015-10-21
|
||||
@@ -76,12 +62,9 @@ property:
|
||||
|
||||
### Verify state restoration
|
||||
|
||||
Verify that the state of your Compose elements is correctly restored when the
|
||||
activity or process is recreated. Perform such checks without relying on
|
||||
activity recreation with the [`StateRestorationTester`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester) class.
|
||||
Verify that the state of your Compose elements is correctly restored when the activity or process is recreated. Perform such checks without relying on activity recreation with the [`StateRestorationTester`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/junit4/StateRestorationTester) class.
|
||||
|
||||
This class lets you simulate the recreation of a composable. It's especially
|
||||
useful to verify the implementation of [`rememberSaveable`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/rememberSaveable.composable#rememberSaveable(kotlin.Array,androidx.compose.runtime.saveable.Saver,kotlin.String,kotlin.Function0)).
|
||||
This class lets you simulate the recreation of a composable. It's especially useful to verify the implementation of [`rememberSaveable`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/rememberSaveable.composable#rememberSaveable(kotlin.Array,androidx.compose.runtime.saveable.Saver,kotlin.String,kotlin.Function0)).
|
||||
|
||||
|
||||
class MyStateRestorationTests {
|
||||
@@ -106,40 +89,25 @@ useful to verify the implementation of [`rememberSaveable`](https://developer.an
|
||||
|
||||
### Test different device configurations
|
||||
|
||||
Android apps need to adapt to many changing conditions: window sizes, locales,
|
||||
font sizes, dark and light themes, and more. Most of these conditions are
|
||||
derived from device-level values controlled by the user and exposed with the
|
||||
current [`Configuration`](https://developer.android.com/reference/android/content/res/Configuration) instance. Testing different configurations
|
||||
directly in a test is difficult since the test must configure device-level
|
||||
properties.
|
||||
Android apps need to adapt to many changing conditions: window sizes, locales, font sizes, dark and light themes, and more. Most of these conditions are derived from device-level values controlled by the user and exposed with the current [`Configuration`](https://developer.android.com/reference/android/content/res/Configuration) instance. Testing different configurations directly in a test is difficult since the test must configure device-level properties.
|
||||
|
||||
[`DeviceConfigurationOverride`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride) is a test-only API that lets you simulate
|
||||
different device configurations in a localized way for the `@Composable` content
|
||||
under test.
|
||||
[`DeviceConfigurationOverride`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride) is a test-only API that lets you simulate different device configurations in a localized way for the `@Composable` content under test.
|
||||
|
||||
The companion object of `DeviceConfigurationOverride` has the following
|
||||
extension functions, which override device-level configuration properties:
|
||||
The companion object of `DeviceConfigurationOverride` has the following extension functions, which override device-level configuration properties:
|
||||
|
||||
- [`DeviceConfigurationOverride.DarkMode()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).DarkMode(kotlin.Boolean)): Overrides the system to dark theme or light theme.
|
||||
- [`DeviceConfigurationOverride.FontScale()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).FontScale(kotlin.Float)): Overrides the [system font
|
||||
scale](https://developer.android.com/training/multiscreen/screendensities#TaskUseDP).
|
||||
- [`DeviceConfigurationOverride.FontScale()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).FontScale(kotlin.Float)): Overrides the [system font scale](https://developer.android.com/training/multiscreen/screendensities#TaskUseDP).
|
||||
- [`DeviceConfigurationOverride.FontWeightAdjustment()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).FontWeightAdjustment(kotlin.Int)): Overrides the system font weight adjustment.
|
||||
- [`DeviceConfigurationOverride.ForcedSize()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).ForcedSize(androidx.compose.ui.unit.DpSize)): Forces a specific amount of space regardless of device size.
|
||||
- [`DeviceConfigurationOverride.LayoutDirection()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).LayoutDirection(androidx.compose.ui.unit.LayoutDirection)): Overrides the [layout
|
||||
direction](https://developer.android.com/training/basics/supporting-devices/languages#SupportLayoutMirroring) (left-to-right or right-to-left).
|
||||
- [`DeviceConfigurationOverride.LayoutDirection()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).LayoutDirection(androidx.compose.ui.unit.LayoutDirection)): Overrides the [layout direction](https://developer.android.com/training/basics/supporting-devices/languages#SupportLayoutMirroring) (left-to-right or right-to-left).
|
||||
- [`DeviceConfigurationOverride.Locales()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).Locales(androidx.compose.ui.text.intl.LocaleList)): Overrides the [locale](https://developer.android.com/guide/topics/resources/localization).
|
||||
- [`DeviceConfigurationOverride.RoundScreen()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.Companion#(androidx.compose.ui.test.DeviceConfigurationOverride.Companion).RoundScreen(kotlin.Boolean)): Overrides if the screen is [round](https://developer.android.com/design/ui/wear/guides/foundations/getting-started#design-for-round).
|
||||
|
||||
To apply a specific override, wrap the content under test in a call to the
|
||||
[`DeviceConfigurationOverride()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.composable#DeviceConfigurationOverride(androidx.compose.ui.test.DeviceConfigurationOverride,kotlin.Function0)) top-level function, passing the override
|
||||
to apply as a parameter.
|
||||
To apply a specific override, wrap the content under test in a call to the [`DeviceConfigurationOverride()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride.composable#DeviceConfigurationOverride(androidx.compose.ui.test.DeviceConfigurationOverride,kotlin.Function0)) top-level function, passing the override to apply as a parameter.
|
||||
|
||||
For example, the following code applies the
|
||||
`DeviceConfigurationOverride.ForcedSize()` override to change the density
|
||||
locally, forcing the `MyScreen` composable to be rendered in a large landscape
|
||||
window, even if the device the test is running on doesn't support that window
|
||||
size directly:
|
||||
For example, the following code applies the `DeviceConfigurationOverride.ForcedSize()` override to change the density locally, forcing the `MyScreen` composable to be rendered in a large landscape window, even if the device the test is running on doesn't support that window size directly:
|
||||
|
||||
<br />
|
||||
|
||||
```kotlin
|
||||
composeTestRule.setContent {
|
||||
@@ -149,13 +117,14 @@ composeTestRule.setContent {
|
||||
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
To apply multiple overrides together, use
|
||||
[`DeviceConfigurationOverride.then()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride#(androidx.compose.ui.test.DeviceConfigurationOverride).then(androidx.compose.ui.test.DeviceConfigurationOverride)):
|
||||
To apply multiple overrides together, use [`DeviceConfigurationOverride.then()`](https://developer.android.com/reference/kotlin/androidx/compose/ui/test/DeviceConfigurationOverride#(androidx.compose.ui.test.DeviceConfigurationOverride).then(androidx.compose.ui.test.DeviceConfigurationOverride)):
|
||||
|
||||
<br />
|
||||
|
||||
```kotlin
|
||||
composeTestRule.setContent {
|
||||
@@ -166,6 +135,7 @@ composeTestRule.setContent {
|
||||
Text(text = "text with increased scale and weight")
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
+28
-65
@@ -1,16 +1,9 @@
|
||||
> [!WARNING]
|
||||
> **Experimental:** Compose Preview Screenshot Testing is still in development. Its features and APIs are subject to change substantially during the alpha phase. Report any feedback and issues through the [issue tracker](https://issuetracker.google.com/issues/new?component=192708&template=840533).
|
||||
|
||||
Screenshot testing is an effective way to verify how your UI looks to users.
|
||||
The Compose Preview Screenshot Testing tool combines the simplicity and
|
||||
features of [composable previews](https://developer.android.com/develop/ui/compose/tooling/previews) with the productivity
|
||||
gains of running host-side screenshot tests. Compose Preview Screenshot Testing
|
||||
is designed to be as straightforward to use as composable previews.
|
||||
Screenshot testing is an effective way to verify how your UI looks to users. The Compose Preview Screenshot Testing tool combines the simplicity and features of [composable previews](https://developer.android.com/develop/ui/compose/tooling/previews) with the productivity gains of running host-side screenshot tests. Compose Preview Screenshot Testing is designed to be as straightforward to use as composable previews.
|
||||
|
||||
A screenshot test is an automated test that takes a screenshot of a piece of UI
|
||||
and then compares it against a previously approved reference image. If the
|
||||
images don't match, the test fails and produces an HTML report to help you
|
||||
compare and find the differences.
|
||||
A screenshot test is an automated test that takes a screenshot of a piece of UI and then compares it against a previously approved reference image. If the images don't match, the test fails and produces an HTML report to help you compare and find the differences.
|
||||
|
||||
With the Compose Preview Screenshot Testing tool, you can:
|
||||
|
||||
@@ -24,31 +17,30 @@ With the Compose Preview Screenshot Testing tool, you can:
|
||||
|
||||
## IDE integration
|
||||
|
||||
While you can use the Compose Preview Screenshot Testing tool by running the
|
||||
underlying Gradle tasks (`updateScreenshotTest` and `validateScreenshotTest`)
|
||||
manually, Android Studio Otter 3 Feature Drop Canary 4 introduces a full IDE
|
||||
integration. This lets you generate reference images, run tests, and analyze
|
||||
validation failures entirely within the IDE. Here are some of the key features:
|
||||
While you can use the Compose Preview Screenshot Testing tool by running the underlying Gradle tasks (`updateScreenshotTest` and `validateScreenshotTest`) manually, Android Studio Otter 3 Feature Drop Canary 4 introduces a full IDE integration. This lets you generate reference images, run tests, and analyze validation failures entirely within the IDE. Here are some of the key features:
|
||||
|
||||
- **In-editor gutter icons.** You can now run tests or update reference images directly from the source code. Green run icons appear in the gutter next to composables and classes annotated with `@PreviewTest`.
|
||||
- **Run screenshot tests.** Execute tests specifically for a single function or for an entire class.
|
||||
- **Add or update reference images.** Trigger the update flow specifically for the selected scope.
|
||||
|
||||
|
||||
- **Interactive reference management.** Updating reference images is now safer and more granular.
|
||||
- **New reference image generation dialog.** Instead of running a bulk Gradle task, a new dialog lets you visualize and select exactly which previews to generate or update.
|
||||
- **Preview variations.** The dialog lists all preview variations (such as light theme or dark theme, or different devices) individually, allowing you to select or clear specific items before generating images.
|
||||
|
||||
|
||||
- **Integrated test results and diff viewer.** View results without leaving the IDE.
|
||||
- **Unified run panel.** Screenshot test results appear in the standard **Run** tool window. Tests are grouped by class and function, with pass or fail status clearly marked.
|
||||
- **Visual diff tool.** When a test fails, the **Screenshot** tab lets you compare the *Reference* , *Actual* , and *Diff* images side-by-side.
|
||||
- **Detailed attributes.** An **Attributes** tab provides metadata on failed tests, including match percentage, image dimensions, and the specific preview configuration used (for example, `uiMode` or `fontScale`).
|
||||
|
||||
|
||||
- **Flexible test scoping.** You can now execute screenshot tests with various scopes directly from the Project View. Right-click a module, directory, file, or class to run screenshot tests specifically for that selection.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
To use Compose Preview Screenshot Testing through the full IDE integration, your
|
||||
project must meet the following requirements:
|
||||
To use Compose Preview Screenshot Testing through the full IDE integration, your project must meet the following requirements:
|
||||
|
||||
- Android Studio Panda 1 Canary 4 or higher.
|
||||
- Android Gradle Plugin (AGP) version 9.0 or higher.
|
||||
@@ -57,8 +49,7 @@ project must meet the following requirements:
|
||||
- JDK version 17 or higher.
|
||||
- Compose enabled for your project. We recommend enabling Compose using the [Compose Compiler Gradle plugin](https://developer.android.com/develop/ui/compose/compiler).
|
||||
|
||||
If you only want to use the underlying Gradle tasks without the IDE integration,
|
||||
the requirements are as follows:
|
||||
If you only want to use the underlying Gradle tasks without the IDE integration, the requirements are as follows:
|
||||
|
||||
- Android Gradle Plugin (AGP) version 8.5.0 or higher.
|
||||
- Compose Preview Screenshot Testing plugin version [0.0.1-alpha15](https://developer.android.com/studio/preview/compose-screenshot-testing-release-notes#alpha15) or higher.
|
||||
@@ -71,22 +62,19 @@ the requirements are as follows:
|
||||
|
||||
## Setup
|
||||
|
||||
Both the integrated tool and the underlying Gradle tasks rely on the Compose
|
||||
Preview Screenshot Testing plugin. To set up the plugin, follow these steps:
|
||||
Both the integrated tool and the underlying Gradle tasks rely on the Compose Preview Screenshot Testing plugin. To set up the plugin, follow these steps:
|
||||
|
||||
1. Enable the experimental property in your project's `gradle.properties` file.
|
||||
|
||||
android.experimental.enableScreenshotTest=true
|
||||
|
||||
2. In the `android {}` block of your module-level `build.gradle.kts` file,
|
||||
enable the experimental flag to use the `screenshotTest` source set.
|
||||
2. In the `android {}` block of your module-level `build.gradle.kts` file, enable the experimental flag to use the `screenshotTest` source set.
|
||||
|
||||
android {
|
||||
experimentalProperties["android.experimental.enableScreenshotTest"] = true
|
||||
}
|
||||
|
||||
3. Add the `com.android.compose.screenshot` plugin, version `0.0.1-alpha15` to
|
||||
your project.
|
||||
3. Add the `com.android.compose.screenshot` plugin, version `0.0.1-alpha15` to your project.
|
||||
|
||||
1. Add the plugin to your version catalogs file:
|
||||
|
||||
@@ -98,16 +86,13 @@ Preview Screenshot Testing plugin. To set up the plugin, follow these steps:
|
||||
[plugins]
|
||||
screenshot = { id = "com.android.compose.screenshot", version.ref = "screenshot"}
|
||||
|
||||
2. In your module-level `build.gradle.kts` file, add the plugin in the
|
||||
`plugins {}` block:
|
||||
2. In your module-level `build.gradle.kts` file, add the plugin in the `plugins {}` block:
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.screenshot)
|
||||
}
|
||||
|
||||
4. Add the [`screenshot-validation-api`](https://maven.google.com/web/index.html?q=screenshot-validation-api#com.android.tools.screenshot:screenshot-validation-api)
|
||||
and [`ui-tooling`](https://maven.google.com/web/index.html?q=tooling#androidx.compose.ui:ui-tooling)
|
||||
dependencies.
|
||||
4. Add the [`screenshot-validation-api`](https://maven.google.com/web/index.html?q=screenshot-validation-api#com.android.tools.screenshot:screenshot-validation-api) and [`ui-tooling`](https://maven.google.com/web/index.html?q=tooling#androidx.compose.ui:ui-tooling) dependencies.
|
||||
|
||||
1. Add them to your version catalogs:
|
||||
|
||||
@@ -124,15 +109,11 @@ Preview Screenshot Testing plugin. To set up the plugin, follow these steps:
|
||||
|
||||
## Designate composable previews to use for screenshot tests
|
||||
|
||||
To designate the composable previews you want to use for screenshot tests, mark
|
||||
the previews with the `@PreviewTest` annotation. The previews must be located in
|
||||
the new `screenshotTest` source set, for example:
|
||||
To designate the composable previews you want to use for screenshot tests, mark the previews with the `@PreviewTest` annotation. The previews must be located in the new `screenshotTest` source set, for example:
|
||||
|
||||
`app/src/screenshotTest/kotlin/com/example/yourapp/`
|
||||
`ExamplePreviewScreenshotTest.kt`
|
||||
`app/src/screenshotTest/kotlin/com/example/yourapp/` `ExamplePreviewScreenshotTest.kt`
|
||||
|
||||
You can add more composables or previews, including multi-previews, in
|
||||
this file or other files created in the same source set.
|
||||
You can add more composables or previews, including multi-previews, in this file or other files created in the same source set.
|
||||
|
||||
package com.example.yourapp
|
||||
|
||||
@@ -152,16 +133,11 @@ this file or other files created in the same source set.
|
||||
|
||||
## Generate reference images
|
||||
|
||||
After you set up a test class, you need to generate reference images for each
|
||||
preview. These reference images are used to identify changes later, after you
|
||||
make code changes. To generate reference images for your composable preview
|
||||
screenshot tests, follow the instructions in this section for the IDE
|
||||
integration or for the Gradle tasks.
|
||||
After you set up a test class, you need to generate reference images for each preview. These reference images are used to identify changes later, after you make code changes. To generate reference images for your composable preview screenshot tests, follow the instructions in this section for the IDE integration or for the Gradle tasks.
|
||||
|
||||
### In the IDE
|
||||
|
||||
Click the gutter icon next to a `@PreviewTest` function and select **Add/Update
|
||||
Reference Images** . Select the previews in the dialog and click **Add**.
|
||||
Click the gutter icon next to a `@PreviewTest` function and select **Add/Update Reference Images** . Select the previews in the dialog and click **Add**.
|
||||
|
||||
### With the Gradle tasks
|
||||
|
||||
@@ -170,46 +146,36 @@ Run the following Gradle task:
|
||||
- Linux and macOS: `./gradlew updateDebugScreenshotTest` (`./gradlew :{module}:update{Variant}ScreenshotTest`)
|
||||
- Windows: `gradlew updateDebugScreenshotTest` (`gradlew :{module}:update{Variant}ScreenshotTest`)
|
||||
|
||||
After the task completes, find the reference images in
|
||||
`app/src/screenshotTestDebug/reference`
|
||||
(`{module}/src/screenshotTest{Variant}/reference`).
|
||||
After the task completes, find the reference images in `app/src/screenshotTestDebug/reference` (`{module}/src/screenshotTest{Variant}/reference`).
|
||||
|
||||
> [!NOTE]
|
||||
> **Note:** The reference images are named with a concatenation of the fully-qualified name of the test function and a hash of the preview parameters, for example `com.sample.screenshottests.test1_da39a3ee_c2200e98_0.png`.
|
||||
|
||||
## Generate a test report
|
||||
|
||||
Once the reference images exist, generate a test report by following the
|
||||
instructions in this section for the IDE integration or for the Gradle tasks.
|
||||
Once the reference images exist, generate a test report by following the instructions in this section for the IDE integration or for the Gradle tasks.
|
||||
|
||||
### In the IDE
|
||||
|
||||
Click the gutter icon next to a `@PreviewTest` function and select **Run
|
||||
'ScreenshotTests'**.
|
||||
Click the gutter icon next to a `@PreviewTest` function and select **Run 'ScreenshotTests'**.
|
||||
|
||||
If a test fails, click the test name in the **Run** panel. Select the
|
||||
**Screenshot** tab to inspect the image diff using the integrated zoom and pan
|
||||
controls.
|
||||
If a test fails, click the test name in the **Run** panel. Select the **Screenshot** tab to inspect the image diff using the integrated zoom and pan controls.
|
||||
|
||||
> [!NOTE]
|
||||
> **Note:** Renaming a function annotated with `@PreviewTest` breaks the association with existing reference images. In that case, you must [regenerate reference images](https://developer.android.com/studio/preview/compose-screenshot-testing#generate-reference-images) for the new function name.
|
||||
|
||||
### With the Gradle tasks
|
||||
|
||||
Run the validate task to take a new screenshot and compare it with the
|
||||
reference image:
|
||||
Run the validate task to take a new screenshot and compare it with the reference image:
|
||||
|
||||
- Linux and macOS: `./gradlew validateDebugScreenshotTest` (`./gradlew :{module}:validate{Variant}ScreenshotTest`)
|
||||
- Windows: `gradlew validateDebugScreenshotTest` (`gradlew :{module}:validate{Variant}ScreenshotTest`)
|
||||
|
||||
The verification task creates an HTML report at
|
||||
`{module}/build/reports/screenshotTest/preview/{variant}/index.html`.
|
||||
The verification task creates an HTML report at `{module}/build/reports/screenshotTest/preview/{variant}/index.html`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Compose Preview Screenshot Testing runs host-side tests, which can be
|
||||
memory-intensive. You can increase the maximum heap size for the test JVM by
|
||||
adding the following property to your `gradle.properties` file:
|
||||
Compose Preview Screenshot Testing runs host-side tests, which can be memory-intensive. You can increase the maximum heap size for the test JVM by adding the following property to your `gradle.properties` file:
|
||||
|
||||
android.compose.screenshot.maxHeapSize=4g
|
||||
|
||||
@@ -217,11 +183,8 @@ adding the following property to your `gradle.properties` file:
|
||||
|
||||
- **Kotlin Multiplatform (KMP):** Both the IDE and the underlying plugin are engineered exclusively for Android projects. They don't support non-Android targets in KMP projects.
|
||||
|
||||
You can find the complete list of current known issues in the tool's
|
||||
[issue tracker component](https://issuetracker.google.com/issues?q=status:open+componentid:1581441&s=created_time:desc). Report any other feedback and issues
|
||||
through the [issue tracker](https://issuetracker.google.com/issues/new?component=192708&template=840533).
|
||||
You can find the complete list of current known issues in the tool's [issue tracker component](https://issuetracker.google.com/issues?q=status:open+componentid:1581441&s=created_time:desc). Report any other feedback and issues through the [issue tracker](https://issuetracker.google.com/issues/new?component=192708&template=840533).
|
||||
|
||||
## Release updates
|
||||
|
||||
For a full list of release updates, see the
|
||||
[release notes](https://developer.android.com/studio/preview/compose-screenshot-testing-release-notes).
|
||||
For a full list of release updates, see the [release notes](https://developer.android.com/studio/preview/compose-screenshot-testing-release-notes).
|
||||
+38
-118
@@ -1,12 +1,8 @@
|
||||
One of the benefits of using dependency injection frameworks like Hilt is that
|
||||
it makes testing your code easier.
|
||||
One of the benefits of using dependency injection frameworks like Hilt is that it makes testing your code easier.
|
||||
|
||||
## Unit tests
|
||||
|
||||
Hilt isn't necessary for unit tests, since when testing a class that uses
|
||||
constructor injection, you don't need to use Hilt to instantiate that class.
|
||||
Instead, you can directly call a class constructor by passing in fake or mock
|
||||
dependencies, just as you would if the constructor weren't annotated:
|
||||
Hilt isn't necessary for unit tests, since when testing a class that uses constructor injection, you don't need to use Hilt to instantiate that class. Instead, you can directly call a class constructor by passing in fake or mock dependencies, just as you would if the constructor weren't annotated:
|
||||
|
||||
```kotlin
|
||||
@ActivityScoped
|
||||
@@ -26,21 +22,15 @@ class AnalyticsAdapterTest {
|
||||
}
|
||||
```
|
||||
|
||||
The same applies to ViewModel classes obtained by calling `hiltViewModel()` in
|
||||
your composables. In unit tests, construct the ViewModel directly with fakes.
|
||||
For information on how state flows from a ViewModel into composables, see
|
||||
[State and Jetpack Compose](https://developer.android.com/develop/ui/compose/state) and [Where to hoist state](https://developer.android.com/develop/ui/compose/state-hoisting).
|
||||
The same applies to ViewModel classes obtained by calling `hiltViewModel()` in your composables. In unit tests, construct the ViewModel directly with fakes. For information on how state flows from a ViewModel into composables, see [State and Jetpack Compose](https://developer.android.com/develop/ui/compose/state) and [Where to hoist state](https://developer.android.com/develop/ui/compose/state-hoisting).
|
||||
|
||||
## End-to-end tests
|
||||
|
||||
For integration tests, Hilt injects dependencies as it would in your production
|
||||
code. Testing with Hilt requires no maintenance because Hilt automatically
|
||||
generates a new set of components for each test.
|
||||
For integration tests, Hilt injects dependencies as it would in your production code. Testing with Hilt requires no maintenance because Hilt automatically generates a new set of components for each test.
|
||||
|
||||
### Adding testing dependencies
|
||||
|
||||
To use Hilt in your tests, include the `hilt-android-testing` dependency in your
|
||||
project:
|
||||
To use Hilt in your tests, include the `hilt-android-testing` dependency in your project:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
@@ -64,11 +54,9 @@ dependencies {
|
||||
|
||||
### UI test setup
|
||||
|
||||
You must annotate any UI test that uses Hilt with `@HiltAndroidTest`. This
|
||||
annotation is responsible for generating the Hilt components for each test.
|
||||
You must annotate any UI test that uses Hilt with `@HiltAndroidTest`. This annotation is responsible for generating the Hilt components for each test.
|
||||
|
||||
Also, you need to add the `HiltAndroidRule` to the test class. It manages the
|
||||
components' state and is used to perform injection on your test:
|
||||
Also, you need to add the `HiltAndroidRule` to the test class. It manages the components' state and is used to perform injection on your test:
|
||||
|
||||
```kotlin
|
||||
@HiltAndroidTest
|
||||
@@ -85,36 +73,21 @@ class SettingsScreenTest {
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> **Note:** If you have other rules in your test, see [Multiple TestRule objects in
|
||||
> your instrumented test](https://developer.android.com/training/dependency-injection/hilt-testing#multiple-testrules).
|
||||
> **Note:** If you have other rules in your test, see [Multiple TestRule objects in your instrumented test](https://developer.android.com/training/dependency-injection/hilt-testing#multiple-testrules).
|
||||
|
||||
Next, your test needs to know about the `Application` class that Hilt
|
||||
automatically generates for you.
|
||||
Next, your test needs to know about the `Application` class that Hilt automatically generates for you.
|
||||
|
||||
To let Hilt inject dependencies, you must create an empty activity named
|
||||
`HiltTestActivity` in your `androidTest` source set and annotate it with
|
||||
`@AndroidEntryPoint`. `createAndroidComposeRule` then uses this activity as the
|
||||
host for your composable content.
|
||||
To let Hilt inject dependencies, you must create an empty activity named `HiltTestActivity` in your `androidTest` source set and annotate it with `@AndroidEntryPoint`. `createAndroidComposeRule` then uses this activity as the host for your composable content.
|
||||
|
||||
#### Test application
|
||||
|
||||
You must execute instrumented tests that use Hilt in an `Application` object
|
||||
that supports Hilt. The library provides `HiltTestApplication` for use in tests.
|
||||
If your tests need a different base application, see [Custom application for
|
||||
tests](https://developer.android.com/training/dependency-injection/hilt-testing#custom-application).
|
||||
You must execute instrumented tests that use Hilt in an `Application` object that supports Hilt. The library provides `HiltTestApplication` for use in tests. If your tests need a different base application, see [Custom application for tests](https://developer.android.com/training/dependency-injection/hilt-testing#custom-application).
|
||||
|
||||
You must set your test application to run in your [instrumented
|
||||
tests](https://developer.android.com/training/testing/ui-testing) or [Robolectric
|
||||
tests](http://robolectric.org/). The following instructions aren't
|
||||
specific to Hilt, but are general guidelines on how to specify a custom
|
||||
application to run in tests.
|
||||
You must set your test application to run in your [instrumented tests](https://developer.android.com/training/testing/ui-testing) or [Robolectric tests](http://robolectric.org/). The following instructions aren't specific to Hilt, but are general guidelines on how to specify a custom application to run in tests.
|
||||
|
||||
##### Set the test application in instrumented tests
|
||||
|
||||
To use the Hilt test application in [instrumented
|
||||
tests](https://developer.android.com/training/testing/ui-testing), you need to configure a new test runner.
|
||||
This makes Hilt work for all of the instrumented tests in your project. Perform
|
||||
the following steps:
|
||||
To use the Hilt test application in [instrumented tests](https://developer.android.com/training/testing/ui-testing), you need to configure a new test runner. This makes Hilt work for all of the instrumented tests in your project. Perform the following steps:
|
||||
|
||||
1. Create a custom class that extends [`AndroidJUnitRunner`](https://developer.android.com/reference/kotlin/androidx/test/runner/AndroidJUnitRunner) in the `androidTest` folder.
|
||||
2. Override the `newApplication` function and pass in the name of the generated Hilt test application.
|
||||
@@ -129,10 +102,7 @@ class CustomTestRunner : AndroidJUnitRunner() {
|
||||
}
|
||||
```
|
||||
|
||||
Next, configure this test runner in your Gradle file as described in the
|
||||
[instrumented unit test
|
||||
guide](https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests#setup). Make sure
|
||||
you use the full classpath:
|
||||
Next, configure this test runner in your Gradle file as described in the [instrumented unit test guide](https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests#setup). Make sure you use the full classpath:
|
||||
|
||||
```kotlin
|
||||
android {
|
||||
@@ -145,13 +115,11 @@ android {
|
||||
|
||||
##### Set the test application in Robolectric tests
|
||||
|
||||
If you use Robolectric to test your UI layer, you can specify which application
|
||||
to use in the `robolectric.properties` file:
|
||||
If you use Robolectric to test your UI layer, you can specify which application to use in the `robolectric.properties` file:
|
||||
|
||||
`application = dagger.hilt.android.testing.HiltTestApplication`
|
||||
|
||||
Alternatively, you can configure the application on each test individually by
|
||||
using Robolectric's `@Config` annotation:
|
||||
Alternatively, you can configure the application on each test individually by using Robolectric's `@Config` annotation:
|
||||
|
||||
```kotlin
|
||||
@HiltAndroidTest
|
||||
@@ -167,13 +135,11 @@ class SettingsScreenTest {
|
||||
|
||||
### Testing features
|
||||
|
||||
Once Hilt is ready to use in your tests, you can use several features to
|
||||
customize the testing process.
|
||||
Once Hilt is ready to use in your tests, you can use several features to customize the testing process.
|
||||
|
||||
#### Inject types in tests
|
||||
|
||||
To inject types into a test, use `@Inject` for field injection. To tell Hilt to
|
||||
populate the `@Inject` fields, call `hiltRule.inject()`.
|
||||
To inject types into a test, use `@Inject` for field injection. To tell Hilt to populate the `@Inject` fields, call `hiltRule.inject()`.
|
||||
|
||||
See the following example of an instrumented test:
|
||||
|
||||
@@ -208,14 +174,9 @@ class SettingsScreenTest {
|
||||
|
||||
#### Replace a binding
|
||||
|
||||
If you need to inject a fake or mock instance of a dependency, you need to tell
|
||||
Hilt not to use the binding that it used in production code and to use a
|
||||
different one instead. To replace a binding, you need to replace the module that
|
||||
contains the binding with a test module that contains the bindings that you want
|
||||
to use in the test.
|
||||
If you need to inject a fake or mock instance of a dependency, you need to tell Hilt not to use the binding that it used in production code and to use a different one instead. To replace a binding, you need to replace the module that contains the binding with a test module that contains the bindings that you want to use in the test.
|
||||
|
||||
For example, suppose your production code declares a binding for
|
||||
`AnalyticsService` as follows:
|
||||
For example, suppose your production code declares a binding for `AnalyticsService` as follows:
|
||||
|
||||
```kotlin
|
||||
@Module
|
||||
@@ -230,10 +191,7 @@ abstract class AnalyticsModule {
|
||||
}
|
||||
```
|
||||
|
||||
To replace the `AnalyticsService` binding in tests, create a new Hilt module in
|
||||
the `test` or `androidTest` folder with the fake dependency and annotate it
|
||||
with `@TestInstallIn`. All the tests in that folder are injected with the fake
|
||||
dependency instead.
|
||||
To replace the `AnalyticsService` binding in tests, create a new Hilt module in the `test` or `androidTest` folder with the fake dependency and annotate it with `@TestInstallIn`. All the tests in that folder are injected with the fake dependency instead.
|
||||
|
||||
```kotlin
|
||||
@Module
|
||||
@@ -251,19 +209,13 @@ abstract class FakeAnalyticsModule {
|
||||
}
|
||||
```
|
||||
|
||||
Because composables typically consume these dependencies indirectly through a
|
||||
ViewModel obtained with `hiltViewModel()`, replacing the binding in Hilt is
|
||||
enough. The composable under test picks up the fake automatically.
|
||||
Because composables typically consume these dependencies indirectly through a ViewModel obtained with `hiltViewModel()`, replacing the binding in Hilt is enough. The composable under test picks up the fake automatically.
|
||||
|
||||
#### Replace a binding in a single test
|
||||
|
||||
To replace a binding in a single test instead of all tests, uninstall a Hilt
|
||||
module from a test using the `@UninstallModules` annotation and create a new
|
||||
test module inside the test.
|
||||
To replace a binding in a single test instead of all tests, uninstall a Hilt module from a test using the `@UninstallModules` annotation and create a new test module inside the test.
|
||||
|
||||
Following the `AnalyticsService` example from the previous version, begin by
|
||||
telling Hilt to ignore the production module by using the `@UninstallModules`
|
||||
annotation in the test class:
|
||||
Following the `AnalyticsService` example from the previous version, begin by telling Hilt to ignore the production module by using the `@UninstallModules` annotation in the test class:
|
||||
|
||||
```kotlin
|
||||
@UninstallModules(AnalyticsModule::class)
|
||||
@@ -271,8 +223,7 @@ annotation in the test class:
|
||||
class SettingsScreenTest { ... }
|
||||
```
|
||||
|
||||
Next, you must replace the binding. Create a new module within the test class
|
||||
that defines the test binding:
|
||||
Next, you must replace the binding. Create a new module within the test class that defines the test binding:
|
||||
|
||||
```kotlin
|
||||
@UninstallModules(AnalyticsModule::class)
|
||||
@@ -294,11 +245,7 @@ class SettingsScreenTest {
|
||||
}
|
||||
```
|
||||
|
||||
This only replaces the binding for a single test class. If you want to replace
|
||||
the binding for all test classes, use the `@TestInstallIn` annotation from the
|
||||
section above. Alternatively, you can put the test binding in the `test` module
|
||||
for Robolectric tests, or in the `androidTest` module for instrumented tests.
|
||||
The recommendation is to use `@TestInstallIn` whenever possible.
|
||||
This only replaces the binding for a single test class. If you want to replace the binding for all test classes, use the `@TestInstallIn` annotation from the section above. Alternatively, you can put the test binding in the `test` module for Robolectric tests, or in the `androidTest` module for instrumented tests. The recommendation is to use `@TestInstallIn` whenever possible.
|
||||
|
||||
> [!WARNING]
|
||||
> **Warning:** You cannot uninstall modules that are not annotated with `@InstallIn`. Attempting to do so causes a compilation error.
|
||||
@@ -311,12 +258,9 @@ The recommendation is to use `@TestInstallIn` whenever possible.
|
||||
|
||||
#### Binding new values
|
||||
|
||||
Use the `@BindValue` annotation to easily bind fields in your test into the Hilt
|
||||
dependency graph. Annotate a field with `@BindValue` and it will be bound under
|
||||
the declared field type with any qualifiers that are present for that field.
|
||||
Use the `@BindValue` annotation to easily bind fields in your test into the Hilt dependency graph. Annotate a field with `@BindValue` and it will be bound under the declared field type with any qualifiers that are present for that field.
|
||||
|
||||
In the `AnalyticsService` example, you can replace `AnalyticsService` with a
|
||||
fake by using `@BindValue`:
|
||||
In the `AnalyticsService` example, you can replace `AnalyticsService` with a fake by using `@BindValue`:
|
||||
|
||||
```kotlin
|
||||
@UninstallModules(AnalyticsModule::class)
|
||||
@@ -330,13 +274,9 @@ class SettingsScreenTest {
|
||||
}
|
||||
```
|
||||
|
||||
This simplifies both replacing a binding and referencing a binding in your test
|
||||
by allowing you to do both at the same time.
|
||||
This simplifies both replacing a binding and referencing a binding in your test by allowing you to do both at the same time.
|
||||
|
||||
`@BindValue` works with qualifiers and other testing annotations. For example,
|
||||
if you use testing libraries such as
|
||||
[Mockito](https://site.mockito.org/), you could use it in a
|
||||
Robolectric test as follows:
|
||||
`@BindValue` works with qualifiers and other testing annotations. For example, if you use testing libraries such as [Mockito](https://site.mockito.org/), you could use it in a Robolectric test as follows:
|
||||
|
||||
```kotlin
|
||||
...
|
||||
@@ -350,10 +290,7 @@ class SettingsScreenTest {
|
||||
}
|
||||
```
|
||||
|
||||
If you need to add a [multibinding](https://dagger.dev/dev-guide/multibindings),
|
||||
you can use the `@BindValueIntoSet` and `@BindValueIntoMap` annotations in place
|
||||
of `@BindValue`. `@BindValueIntoMap` requires you to also annotate the field
|
||||
with a map key annotation.
|
||||
If you need to add a [multibinding](https://dagger.dev/dev-guide/multibindings), you can use the `@BindValueIntoSet` and `@BindValueIntoMap` annotations in place of `@BindValue`. `@BindValueIntoMap` requires you to also annotate the field with a map key annotation.
|
||||
|
||||
## Special cases
|
||||
|
||||
@@ -361,36 +298,23 @@ Hilt also provides features to support nonstandard use cases.
|
||||
|
||||
### Custom application for tests
|
||||
|
||||
If you cannot use `HiltTestApplication` because your test application needs to
|
||||
extend another application, annotate a new class or interface with
|
||||
`@CustomTestApplication`, passing in the value of the base class you want the
|
||||
generated Hilt application to extend.
|
||||
If you cannot use `HiltTestApplication` because your test application needs to extend another application, annotate a new class or interface with `@CustomTestApplication`, passing in the value of the base class you want the generated Hilt application to extend.
|
||||
|
||||
`@CustomTestApplication` will generate an `Application` class ready for testing
|
||||
with Hilt that extends the application you passed as a parameter.
|
||||
`@CustomTestApplication` will generate an `Application` class ready for testing with Hilt that extends the application you passed as a parameter.
|
||||
|
||||
```kotlin
|
||||
@CustomTestApplication(BaseApplication::class)
|
||||
interface HiltTestApplication
|
||||
```
|
||||
|
||||
In the example, Hilt generates an `Application` named
|
||||
`HiltTestApplication_Application` that extends the `BaseApplication` class. In
|
||||
general, the name of the generated application is the name of the annotated
|
||||
class appended with `_Application`. You must set the generated Hilt test
|
||||
application to run in your [instrumented tests](https://developer.android.com/training/testing/ui-testing) or
|
||||
[Robolectric tests](http://robolectric.org/) as described in [Test
|
||||
application](https://developer.android.com/training/dependency-injection/hilt-testing#test-application).
|
||||
In the example, Hilt generates an `Application` named `HiltTestApplication_Application` that extends the `BaseApplication` class. In general, the name of the generated application is the name of the annotated class appended with `_Application`. You must set the generated Hilt test application to run in your [instrumented tests](https://developer.android.com/training/testing/ui-testing) or [Robolectric tests](http://robolectric.org/) as described in [Test application](https://developer.android.com/training/dependency-injection/hilt-testing#test-application).
|
||||
|
||||
> [!NOTE]
|
||||
> **Note:** Because `HiltTestApplication_Application` is code that Hilt generates at runtime, the IDE might highlight it in red until you run your tests.
|
||||
|
||||
### Multiple TestRule objects in your instrumented test
|
||||
|
||||
Compose UI tests already combine `HiltAndroidRule` with a Compose test rule
|
||||
such as `createAndroidComposeRule`. If you have additional `TestRule` objects,
|
||||
make sure `HiltAndroidRule` runs first. Declare the execution order with the
|
||||
`order` attribute on `@Rule`:
|
||||
Compose UI tests already combine `HiltAndroidRule` with a Compose test rule such as `createAndroidComposeRule`. If you have additional `TestRule` objects, make sure `HiltAndroidRule` runs first. Declare the execution order with the `order` attribute on `@Rule`:
|
||||
|
||||
```kotlin
|
||||
@HiltAndroidTest
|
||||
@@ -409,8 +333,7 @@ class SettingsScreenTest {
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can wrap the rules with `RuleChain`, placing
|
||||
`HiltAndroidRule` as the outer rule.
|
||||
Alternatively, you can wrap the rules with `RuleChain`, placing `HiltAndroidRule` as the outer rule.
|
||||
|
||||
```kotlin
|
||||
@HiltAndroidTest
|
||||
@@ -426,12 +349,9 @@ class SettingsScreenTest {
|
||||
|
||||
### Use an entry point before the singleton component is available
|
||||
|
||||
The `@EarlyEntryPoint` annotation provides an escape hatch when a Hilt entry
|
||||
point needs to be created before the singleton component is available in a
|
||||
Hilt test.
|
||||
The `@EarlyEntryPoint` annotation provides an escape hatch when a Hilt entry point needs to be created before the singleton component is available in a Hilt test.
|
||||
|
||||
More information about `@EarlyEntryPoint` in the
|
||||
[Hilt documentation](https://dagger.dev/hilt/early-entry-point).
|
||||
More information about `@EarlyEntryPoint` in the [Hilt documentation](https://dagger.dev/hilt/early-entry-point).
|
||||
|
||||
## Additional resources
|
||||
|
||||
|
||||
Reference in New Issue
Block a user