jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 1 | # Android Instrumentation Tests |
| 2 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 3 | Instrumentation tests are JUnit 4 tests that run on devices or emulators. They |
| 4 | can be either unit tests or integration test. |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 5 | |
| 6 | [TOC] |
| 7 | |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 8 | ## Unit vs Integration Tests |
| 9 | |
| 10 | **When the code under test is library-like code**, mainly defined by its |
| 11 | contract, intended to be used in many contexts, internally complex, and related |
| 12 | bugs are more likely to be introduced to the logic inside it than due to |
| 13 | interactions with other code, **write unit tests**. Unit tests are mostly |
| 14 | Robolectric (`junit` build targets), but some are Instrumentation tests |
| 15 | (`unit_device_javatests` build targets). |
| 16 | |
| 17 | **When the code under test is application-like code**, mainly defined by its |
| 18 | behavior, intended to be used in a small number of contexts (often only one), |
| 19 | internally simple, and related bugs are more likely to be in how/when it is |
| 20 | called or due to interactions with other code rather its internal business |
| 21 | logic, **write integration tests**. Integration tests are all Instrumentation |
| 22 | tests (`javatests` build targets). |
| 23 | |
| 24 | ## Robolectric (workstation) vs Instrumentation (on-device) Tests |
| 25 | |
| 26 | When a unit test doesn't require rendering or interacting with the native |
| 27 | library, Robolectric tests, which run on workstations, are preferable to |
| 28 | instrumentation tests, which run on devices and emulators. Robolectric tests are |
| 29 | faster to compile and run. |
| 30 | |
| 31 | See [android_robolectric_tests.md] for more info on Robolectric tests. |
| 32 | |
| 33 | For integration app-like tests, use an Instrumentation test. |
| 34 | |
| 35 | Where supported, use the existing [Transit |
| 36 | Layer](https://source.chromium.org/chromium/chromium/src/+/main:chrome/test/android/javatests/src/org/chromium/chrome/test/transit) |
| 37 | to navigate through the app. |
| 38 | |
| 39 | Where not supported yet, expand the Transit Layer by defining the expected app |
| 40 | behavior. See the |
| 41 | [Public Transit README.md](/base/test/android/javatests/src/org/chromium/base/test/transit/README.md) |
| 42 | and follow examples in the Transit Layer. |
| 43 | |
| 44 | ## Examples of Instrumentation Tests |
| 45 | |
| 46 | * Instrumentation Integration test example: |
| 47 | [ExampleFreshCtaTest](/chrome/android/javatests/src/org/chromium/chrome/browser/ExampleFreshCtaTest.java) |
| 48 | * Instrumentation Unit test example: |
| 49 | [ExampleOnDeviceUnitTest](/chrome/android/javatests/src/org/chromium/chrome/browser/ExampleOnDeviceUnitTest.java) |
| 50 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 51 | ## Tracing |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 52 | |
Nick Ward | c650fdf | 2017-08-12 01:13:42 | [diff] [blame] | 53 | Enabling tracing during a test run allows all the function calls involved to be |
| 54 | observed in a visual display (using Chrome's built-in chrome://tracing feature). |
| 55 | To run a test with tracing, add the `--trace-output` flag to the command used to |
| 56 | call the instrumentation test (either running the test_runner.py script, or a |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 57 | generated binary such as `run_chrome_public_test_apk`). The `--trace-output` |
| 58 | flag takes a filename, which, after the test run, will contain a JSON file |
| 59 | readable by chrome://tracing. |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 60 | |
Nick Ward | c650fdf | 2017-08-12 01:13:42 | [diff] [blame] | 61 | By default, the trace includes only certain function calls important to the test |
| 62 | run, both within the Python test runner framework and the Java code running on |
| 63 | the device. For a more detailed look, add the (no-argument) `--trace-all` flag. |
| 64 | This causes every function called on the Python side to be added to the trace. |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 65 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 66 | ## Test Batching Annotations |
Michael Thiessen | 6bafbf0 | 2020-05-25 20:49:35 | [diff] [blame] | 67 | |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 68 | In Robolectric, all tests are effectively batched. |
| 69 | |
| 70 | For Instrumentation tests, see [batching_instrumentation_tests.md] or the summary below: |
| 71 | |
| 72 | The |
| 73 | [`@Batch`](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java) |
Michael Thiessen | 6bafbf0 | 2020-05-25 20:49:35 | [diff] [blame] | 74 | annotation is used to run all tests with the same batch group name in the same |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 75 | instrumentation invocation. In other words, the browser process is not restarted |
| 76 | between these tests, and so most changes to global state, like launching an |
| 77 | Activity, may persist between tests within a batch group. The benefit of this is |
| 78 | that these tests run significantly faster - the per-test cost of restarting the |
| 79 | process can be as high as 10 seconds (usually around 2 seconds), and that |
| 80 | doesn't count the cost of starting an Activity like ChromeTabbedActivity. |
Michael Thiessen | 6bafbf0 | 2020-05-25 20:49:35 | [diff] [blame] | 81 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 82 | ## Size Annotations |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 83 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 84 | Size annotations are [used by the test runner] to determine the length of time |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 85 | to wait before considering a test hung (i.e., its timeout duration), however the |
| 86 | limits aren't very different currently because all tests get a 60s timeout |
| 87 | added. |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 88 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 89 | Annotations from `androidx.test.filters`: |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 90 | |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 91 | - [`@SmallTest`](https://developer.android.com/reference/androidx/test/filters/SmallTest.html) (timeout: **10 (+60) seconds**) |
| 92 | - [`@MediumTest`](https://developer.android.com/reference/androidx/test/filters/MediumTest.html) (timeout: **30 (+60) seconds**) |
| 93 | - [`@LargeTest`](https://developer.android.com/reference/androidx/test/filters/LargeTest.html) (timeout: **2 (+1) minutes**) |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 94 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 95 | Annotations from `//base`: |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 96 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 97 | - [`@EnormousTest`](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java) |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 98 | (timeout: **5 (+1) minutes**) Typically used for tests that require WiFi. |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 99 | - [`@IntegrationTest`](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java) |
Henrique Nakashima | 9754600 | 2025-03-26 18:15:46 | [diff] [blame] | 100 | (timeout: **10 (+1) minutes**) Used for tests that run against real services. |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 101 | - [`@Manual`](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/Manual.java) |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 102 | (timeout: **10 hours**) Used for manual tests. |
| 103 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 104 | [used by the test runner]: https://source.chromium.org/search?q=file:local_device_instrumentation_test_run.py%20symbol:TIMEOUT_ANNOTATIONS&sq=&ss=chromium |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 105 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 106 | ## Annotations that Disable Tests |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 107 | |
| 108 | There are several annotations that control whether or not a test runs. |
| 109 | Some are conditional, others are not. |
| 110 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 111 | ### Unconditional Disabling |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 112 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 113 | [**@DisabledTest**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 114 | unconditionally disables a test. |
| 115 | ```java |
| 116 | @DisabledTest( |
| 117 | // Describes why the test is disabled. Typically includes a crbug link. |
| 118 | message = "" |
| 119 | ) |
| 120 | ``` |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 121 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 122 | ### Conditional Disabling |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 123 | |
| 124 | There are two primary annotation categories that conditionally disable tests: |
| 125 | **@DisableIf** and **@Restriction**. The **@DisableIf** annotations are intended |
| 126 | to temporarily disable a test in certain scenarios where it *should* work but |
| 127 | doesn't. In contrast, the **@Restriction** annotation is intended to |
| 128 | permanently limit a test to specific configurations. It signifies that the test |
| 129 | was not, is not, and will not be intended to run beyond those configurations. |
| 130 | In both cases, conditional disabling manifests as a skipped test. |
| 131 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 132 | [**@DisableIf.Build**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/DisableIf.java#25) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 133 | allows for conditional test disabling based on values in |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 134 | [`android.os.Build`](https://developer.android.com/reference/android/os/Build.html): |
| 135 | |
| 136 | ```java |
| 137 | @DisableIf.Build( |
| 138 | |
| 139 | // Describes why the test is disabled. |
| 140 | message = "", |
| 141 | |
| 142 | // Disables the test on SDK levels that match the given conditions. |
| 143 | // Checks against Build.VERSION.SDK_INT. |
| 144 | sdk_is_greater_than = 0, |
| 145 | sdk_is_less_than = Integer.MAX_VALUE, |
| 146 | |
| 147 | // Disables the test on devices that support the given ABI |
| 148 | // (e.g. "arm64-v8a"). Checks against: |
| 149 | // - Build.SUPPORTED_ABIS on L+ |
| 150 | // - Build.CPU_ABI and Build.CPU_ABI2 otherwise |
| 151 | supported_abis_includes = "", |
| 152 | |
| 153 | // Disables the test on devices with hardware that matches the given |
| 154 | // value. Checks against Build.HARDWARE. |
| 155 | hardware_is = "", |
| 156 | |
| 157 | // Disables the test on devices with product names that contain the |
| 158 | // given value. Checks against Build.PRODUCT. |
| 159 | product_name_includes = "", |
| 160 | |
| 161 | ) |
| 162 | ``` |
| 163 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 164 | [**@DisableIf.Device**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/DisableIf.java#40) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 165 | allows for conditional test disabling based on whether |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 166 | a device is a phone, a tablet, or a "large tablet" as determined by |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 167 | [org.chromium.ui.base.DeviceFormFactor](https://chromium.googlesource.com/chromium/src/+/main/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java). |
Shenghua Zhang | cac866d | 2017-08-29 01:30:55 | [diff] [blame] | 168 | This is available to tests in |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 169 | [//ui](https://chromium.googlesource.com/chromium/src/+/main/ui/) |
Shenghua Zhang | cac866d | 2017-08-29 01:30:55 | [diff] [blame] | 170 | or code that uses //ui. |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 171 | |
| 172 | ```java |
| 173 | @DisableIf.Device( |
| 174 | // Disables the test on devices that match the given type(s) as described |
| 175 | // above. |
| 176 | type = {} |
| 177 | ) |
| 178 | ``` |
| 179 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 180 | [**@Restriction**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/Restriction.java) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 181 | currently allows for conditional test disabling based on device |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 182 | type, device performance, internet connectivity, whether Google Play Services is |
| 183 | up to date, and whether the build was an official one. |
| 184 | |
| 185 | ```java |
| 186 | @Restriction( |
| 187 | // Possible values include: |
| 188 | // |
Michael Thiessen | 6bafbf0 | 2020-05-25 20:49:35 | [diff] [blame] | 189 | // base: |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 190 | // - Restriction.RESTRICTION_TYPE_LOW_END_DEVICE |
| 191 | // Restricts the test to low-end devices as determined by SysUtils.isLowEndDevice(). |
| 192 | // |
| 193 | // - Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE |
| 194 | // Restricts the test to non-low-end devices as determined by SysUtils.isLowEndDevice(). |
| 195 | // |
| 196 | // - Restriction.RESTRICTION_TYPE_INTERNET |
| 197 | // Restricts the test to devices that have an internet connection. |
| 198 | // |
| 199 | // chrome: |
| 200 | // - ChromeRestriction.RESTRICTION_TYPE_GOOGLE_PLAY_SERVICES |
| 201 | // Restricts the test to devices with up-to-date versions of Google Play Services. |
| 202 | // |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 203 | // - ChromeRestriction.RESTRICTION_TYPE_OFFICIAL_BUILD |
| 204 | // Restricts the test to official builds as determined by ChromeVersionInfo.isOfficialBuild(). |
Shenghua Zhang | cac866d | 2017-08-29 01:30:55 | [diff] [blame] | 205 | // |
| 206 | // ui: |
| 207 | // - UiRestriction.RESTRICTION_TYPE_PHONE |
| 208 | // Restricts the test to phones as determined by DeviceFormFactor. |
| 209 | // |
| 210 | // - UiRestriction.RESTRICTION_TYPE_TABLET |
| 211 | // Restricts the test to tablets as determined by DeviceFormFactor. |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 212 | value = {} |
| 213 | ) |
| 214 | ``` |
| 215 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 216 | [**@MinAndroidSdkLevel**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 217 | is similar to **@Restriction** in purpose in that it's |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 218 | intended to permanently limit a test to only recent versions of Android. |
| 219 | |
| 220 | ```java |
| 221 | @MinAndroidSdkLevel( |
| 222 | // The minimum SDK level at which this test should be executed. Checks |
| 223 | // against Build.VERSION.SDK_INT. |
| 224 | value = 0 |
| 225 | ) |
| 226 | ``` |
| 227 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 228 | ## Command-Line Flags Annotations |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 229 | |
| 230 | Several annotations affect how a test is run in interesting or nontrivial ways. |
| 231 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 232 | [**@CommandLineFlags.Add**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java#46) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 233 | and |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 234 | [**@CommandLineFlags.Remove**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java#58) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 235 | manipulate Chrome's |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 236 | command-line flags on a per-test basis (i.e., the flags handled by |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 237 | [`org.chromium.base.CommandLine`](https://chromium.googlesource.com/chromium/src/+/main/base/android/java/src/org/chromium/base/CommandLine.java) and |
| 238 | [`base::CommandLine`](https://chromium.googlesource.com/chromium/src/+/main/base/command_line.h)). |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 239 | |
| 240 | ```java |
| 241 | @CommandLineFlags.Add( |
| 242 | // The flags to add to the command line for this test. These can be |
| 243 | // anything and typically should include the leading dashes (e.g. "--foo"). |
| 244 | value = {} |
| 245 | ) |
| 246 | |
| 247 | @CommandLineFlags.Remove( |
| 248 | // The flags to remove from the command line for this test. These can only |
| 249 | // be flags added via @CommandLineFlags.Add. Flags already present in the |
| 250 | // command-line file on the device are only present in the native |
| 251 | // CommandLine object and cannot be manipulated. |
| 252 | value = {} |
| 253 | ) |
| 254 | ``` |
| 255 | |
Andrew Grieve | a971215f | 2023-12-12 19:22:49 | [diff] [blame] | 256 | ## Feature Annotations |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 257 | |
John Palmer | a8515fca | 2021-05-20 03:35:32 | [diff] [blame] | 258 | [**@Feature**](https://chromium.googlesource.com/chromium/src/+/main/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java) |
jbudorick | a2d64d1 | 2016-08-22 22:10:00 | [diff] [blame] | 259 | has been used inconsistently in Chromium to group tests across |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 260 | test cases according to the feature they're testing. |
| 261 | |
| 262 | ```java |
| 263 | @Feature( |
| 264 | // The features associated with this test. These can be anything. |
| 265 | value = {} |
| 266 | ) |
| 267 | ``` |
| 268 | |
| 269 | @Feature doesn't have an inherent function, but it can be used to filter tests |
| 270 | via the test runner's |
Andrew Grieve | b5b3bb3 | 2023-10-04 13:55:51 | [diff] [blame] | 271 | `-A/--annotation` and `-E/--exclude-annotation` flags. For example, this would |
| 272 | run only the tests with @Feature annotations containing at least "Sync" in |
| 273 | `chrome_public_test_apk`: |
jbudorick | 5a9b37f5 | 2016-08-17 00:36:43 | [diff] [blame] | 274 | |
| 275 | ```bash |
| 276 | out/Debug/bin/run_chrome_public_test_apk -A Feature=Sync |
| 277 | ``` |