Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 1 | # Automated testing for Chrome for iOS |
| 2 | |
| 3 | See the [instructions] for how to check out and build Chromium for iOS. |
| 4 | |
| 5 | Automated testing is a crucial part of ensuring the quality of Chromium. |
| 6 | |
| 7 | ## Unit testing |
| 8 | |
| 9 | Unit testing is done via gtests. To run a unit test, simply run the test |
| 10 | target (ending in _unittest). |
| 11 | |
| 12 | ## Integration testing |
| 13 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 14 | [EarlGrey] (EG2) is the integration testing framework used by Chromium for iOS. |
| 15 | |
| 16 | ### Writing EarlGrey tests |
| 17 | |
| 18 | #### Before you start |
| 19 | |
| 20 | * Just write a unit test if the purpose of your test does not involve UI. |
| 21 | * Learn about EarlGrey test framework principles and APIs in [EarlGrey]. |
| 22 | * Learn about [Defining Test Cases and Test Methods] from Apple. |
| 23 | |
| 24 | #### Creating test files and writing EG2 tests |
| 25 | |
| 26 | 1. EG2 test files are ended with _egtest.mm, and usually located within the same |
| 27 | directory of the UI code you wish to test. |
| 28 | 2. Basic imports of a EG2 test file: |
| 29 | |
| 30 | * You’ll have to include: |
| 31 | ``` |
Ernesto Izquierdo Clua | ab3923fd | 2022-03-01 20:42:15 | [diff] [blame] | 32 | #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 33 | ``` |
| 34 | * You’ll most likely find util functions in these files helpful. |
| 35 | ``` |
| 36 | #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
| 37 | #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" |
| 38 | #import "ios/chrome/test/earl_grey/chrome_matchers.h" |
| 39 | ``` |
| 40 | * Beside these, directly import an EG2 header for an EG2 API you are using. |
| 41 | |
| 42 | 3. TestCase/testMethods definitions. Create `SomeGreatTestCase` as a subclass of |
| 43 | `ChromeTestCase`. Create test methods, eg `-(void)testMyGreatUIFeature {...}`, |
| 44 | and put UI actions within the test methods. |
| 45 | * Put your setup and tear down code for the *TestCase* in |
| 46 | `+(void)setUpForTestCase` and `+tearDown`. These will run once before and |
| 47 | after all tests for the test class. |
| 48 | * Put your setup and tear down code for each *test method* in `-(void)setUp` |
| 49 | and `-(void)tearDown`. These will run before and after every |
| 50 | `-(void)testMethod` in the file. |
| 51 | 4. Writing test contents. See the chrome helpers (imports in 2.) as well as |
| 52 | [EarlGrey APIs] to write a UI action/assertion in your testMethod. |
| 53 | |
| 54 | #### Interacting with the app in a test |
| 55 | |
| 56 | ##### Relaunch app with different flags |
| 57 | |
| 58 | In EG2 tests, the test process launches the host app process at the beginning, |
| 59 | then runs UI actions/assertions in the app. To pass args or feature flags to the |
| 60 | app at initial launching, or relaunch the app in the middle of your test, see |
| 61 | [this AppLaunchManager API]. |
| 62 | |
| 63 | ##### Accessing app internals |
| 64 | |
| 65 | EG2 test targets are built with test-related code but without app code. |
| 66 | |
| 67 | To access anything from the app side, use an "app interface". App interface is |
| 68 | implemented as a class that lives in the app process, but can be accessed in the |
| 69 | test process through [eDO]. You can include the header in your test side code |
| 70 | and call class methods of the interface class. The methods will execute code in |
| 71 | the app process and can return basic Objective-C types. See this [Example of App |
| 72 | Interface]. |
| 73 | |
| 74 | See `eg_test_support+eg2` (test side utilities) and `eg_app_support+eg2` (app |
| 75 | side utilities) targets in `BUILD.gn` files to learn how test utilities are |
| 76 | organized in targets. If you added an app side helper (app interface), you’ll |
| 77 | also need to include your new `eg_app_support+eg2` target in |
| 78 | `//ios/chrome/test/earl_grey/BUILD.gn`’s `eg_app_support+eg2` target. ([Example |
| 79 | CL adding App Interface]). |
| 80 | |
| 81 | Note that if you create an App interface, you can’t build the app interface |
Sylvain Defresne | a852434c | 2021-10-15 07:53:39 | [diff] [blame] | 82 | class in your eg2_tests target, but you need to include and refer to it. To |
| 83 | satisfy the linker, you'll need to create a `my_test_app_interface_stub.mm` |
| 84 | file with the following content in it and build it as a dependency of your |
| 85 | tests that use the app interface. |
| 86 | |
| 87 | ```objc |
| 88 | #import "ios_internal/chrome/test/earl_grey2/my_test_app_interface.h" |
| 89 | |
Cameron Higgins | 0e35019 | 2023-03-21 17:43:45 | [diff] [blame] | 90 | #import "ios/testing/earl_grey/earl_grey_test.h" |
Sylvain Defresne | a852434c | 2021-10-15 07:53:39 | [diff] [blame] | 91 | |
| 92 | #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 93 | #error "This file requires ARC support." |
| 94 | #endif |
| 95 | |
| 96 | GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(MyTestAppInterface) |
| 97 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 98 | ``` |
Sylvain Defresne | a852434c | 2021-10-15 07:53:39 | [diff] [blame] | 99 | |
| 100 | If you don't you'll get linker errors that read like “Undefined symbols for |
| 101 | architecture… MyTestAppInterface” |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 102 | |
| 103 | #### Creating test targets and adding the target to test suites |
| 104 | |
| 105 | 1. Create a test target. Add a target(`source_set`) named "eg2_tests" into the |
| 106 | closest `BUILD.gn` file. Put the test file into the `sources` array and put the |
| 107 | targets containing headers used in your test file into `deps` array. This is to |
| 108 | organize test source files and dependencies so that the GN build system can |
| 109 | correctly build the test module. The skeleton of the target: |
| 110 | ``` |
| 111 | source_set("eg2_tests") { |
| 112 | configs += [ |
| 113 | "//build/config/compiler:enable_arc", |
| 114 | "//build/config/ios:xctest_config", |
| 115 | ] |
| 116 | testonly = true |
| 117 | sources = [ |
| 118 | "some_egtest.mm" |
| 119 | ] |
| 120 | deps = [ |
| 121 | "//ios/chrome/test/earl_grey:eg_test_support+eg2", |
| 122 | "//ios/testing/earl_grey:eg_test_support+eg2", |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 123 | ] |
Arthur Milchior | 54e9dc2 | 2022-09-21 13:41:52 | [diff] [blame] | 124 | frameworks = [ "UIKit.framework" ] |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 125 | } |
| 126 | ``` |
| 127 | 2. Include your test target in the `deps` array of a suitable suite in |
| 128 | `//src/ios/chrome/test/earl_grey2/BUILD.gn`. |
| 129 | 3. Optional: If you feel like your new test should be in a new suite, or you |
| 130 | want to delete an existing suite to make tests better organized, you’ll need to |
| 131 | change the suites in `//src/ios/chrome/test/earl_grey2/BUILD.gn` in the format |
| 132 | of existing ones. (Do not forget to [config the bots] so the new suite can run |
| 133 | in infra.) |
| 134 | 4. Ensure your dependencies are correct. |
| 135 | ``` |
| 136 | $ gn gen --check out/Debug-iphonesimulator |
| 137 | ``` |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 138 | |
| 139 | ### Running EarlGrey tests |
| 140 | |
| 141 | EarlGrey tests are based on Apple's [XCUITest]. |
| 142 | |
| 143 | #### Running tests from Xcode |
| 144 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 145 | 1. If you added a new test file / suite, run `gclient runhooks` to sync for the |
| 146 | list of tests in Xcode. |
Victor Hugo Vianna Silva | 703246b | 2023-07-05 14:06:46 | [diff] [blame] | 147 | 2. Run a test suite(module), TestCase or testMethod in test navigator. |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 148 | Xcode will build the targets and run the test(s) you choose. Alternatively, |
| 149 | use ⌘+U to run all the tests. See Apple's [Running Tests and Viewing Results]. |
Victor Hugo Vianna Silva | 703246b | 2023-07-05 14:06:46 | [diff] [blame] | 150 | 3. You can pass extra arguments to the app process with `--extra-app-args`, e.g. |
| 151 | `--extra-app-args='--enable-features=Foo'`. |
| 152 | * This might not work consistently as tests can re-launch the app with |
| 153 | arbitrary command-line arguments. |
| 154 | |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 155 | |
| 156 | #### Running from the command-line |
| 157 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 158 | EG2 tests can run in the command line with test runner scripts. You’ll need to |
| 159 | build the targets before running tests in cmd. This is used by continuous |
| 160 | integration infra and thus not user friendly. Running UI tests directly in Xcode |
| 161 | is recommended. |
| 162 | |
| 163 | Important notes: |
| 164 | * The test runner can invoke mac_toolchain to install a new Xcode of the version |
| 165 | specified to the path specified. You may want to choose a different path from |
| 166 | your daily use Xcode. |
| 167 | * If test_cases is empty in --args-json, all tests will run. Specifying a |
| 168 | testMethod to run is currently not supported in the test runner. |
| 169 | |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 170 | Example: |
| 171 | ``` |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 172 | src/ios/build/bots/scripts/run.py |
| 173 | --app |
| 174 | src/out/Debug-iphonesimulator/ios_chrome_ui_eg2tests_module-Runner.app |
| 175 | --host-app |
| 176 | src/out/Debug-iphonesimulator/ios_chrome_eg2tests.app |
| 177 | --args-json |
| 178 | {"test_args": [], "xctest": false, "test_cases": ["ReadingListTestCase"], |
| 179 | "restart": false, "xcode_parallelization": true, "xcodebuild_device_runner": |
| 180 | false} |
| 181 | --out-dir |
| 182 | path/to/output/dir |
| 183 | --retries |
| 184 | 3 |
| 185 | --shards |
| 186 | 1 |
| 187 | --xcode-build-version |
| 188 | 11c29 |
| 189 | --mac-toolchain-cmd |
| 190 | path/to/mac_toolchain |
| 191 | --xcode-path |
| 192 | path/to/Xcode.app |
| 193 | --wpr-tools-path |
| 194 | NO_PATH |
| 195 | --replay-path |
| 196 | NO_PATH |
| 197 | --iossim |
| 198 | src/out/Debug-iphonesimulator/iossim |
| 199 | --platform |
| 200 | iPad (6th generation) |
| 201 | --version |
| 202 | 13.3 |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 203 | ``` |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 204 | The invocation args are logged. You can find the latest arg format at the |
| 205 | beginning of stdout from an infra test shard if the above doesn't work. |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 206 | |
| 207 | |
John Palmer | 046f987 | 2021-05-24 01:24:56 | [diff] [blame] | 208 | [config the bots]: https://chromium.googlesource.com/chromium/src/testing/+/refs/heads/main/buildbot/README.md#buildbot-testing-configuration-files |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 209 | [Defining Test Cases and Test Methods]: https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods?language=objc |
| 210 | [EarlGrey]: https://github.com/google/EarlGrey/tree/earlgrey2 |
| 211 | [EarlGrey APIs]: https://github.com/google/EarlGrey/blob/master/docs/api.md |
| 212 | [eDO]: https://github.com/google/eDistantObject |
| 213 | [Example of App Interface]: https://cs.chromium.org/chromium/src/ios/chrome/browser/metrics/metrics_app_interface.h |
| 214 | [Example CL adding App Interface]: https://chromium-review.googlesource.com/c/chromium/src/+/1919147 |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 215 | [instructions]: ./build_instructions.md |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 216 | [Running Tests and Viewing Results]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/05-running_tests.html |
John Palmer | 046f987 | 2021-05-24 01:24:56 | [diff] [blame] | 217 | [this AppLaunchManager API]: https://source.chromium.org/chromium/chromium/src/+/main:ios/testing/earl_grey/app_launch_manager.h;drc=d0889865de20c5b3bc59d58674eb2dcc02dd2269;l=47 |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 218 | [XCUITest]: https://developer.apple.com/documentation/xctest |