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 | ``` |
| 32 | #import "ios/chrome/test/earl_grey/chrome_test_case.h |
| 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 |
| 82 | class in your eg2_tests target, but you need to include and refer to it. If you |
| 83 | see a “Undefined symbols for architecture… MyTestAppInterface”, add |
| 84 | ``` |
| 85 | #if defined(CHROME_EARL_GREY_2) |
| 86 | // TODO(crbug.com/1015113) The EG2 macro is breaking indexing for some reason |
| 87 | // without the trailing semicolon. For now, disable the extra semi warning |
| 88 | // so Xcode indexing works for the egtest. |
| 89 | #pragma clang diagnostic push |
| 90 | #pragma clang diagnostic ignored "-Wc++98-compat-extra-semi" |
| 91 | GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(HandoffManagerAppInterface); |
| 92 | #pragma clang diagnostic pop |
| 93 | #endif // defined(CHROME_EARL_GREY_2) |
| 94 | ``` |
| 95 | to the top of your foo_egtest.mm file. |
| 96 | |
| 97 | #### Creating test targets and adding the target to test suites |
| 98 | |
| 99 | 1. Create a test target. Add a target(`source_set`) named "eg2_tests" into the |
| 100 | closest `BUILD.gn` file. Put the test file into the `sources` array and put the |
| 101 | targets containing headers used in your test file into `deps` array. This is to |
| 102 | organize test source files and dependencies so that the GN build system can |
| 103 | correctly build the test module. The skeleton of the target: |
| 104 | ``` |
| 105 | source_set("eg2_tests") { |
| 106 | configs += [ |
| 107 | "//build/config/compiler:enable_arc", |
| 108 | "//build/config/ios:xctest_config", |
| 109 | ] |
| 110 | testonly = true |
| 111 | sources = [ |
| 112 | "some_egtest.mm" |
| 113 | ] |
| 114 | deps = [ |
| 115 | "//ios/chrome/test/earl_grey:eg_test_support+eg2", |
| 116 | "//ios/testing/earl_grey:eg_test_support+eg2", |
| 117 | "//ios/third_party/earl_grey2:test_lib", |
| 118 | ] |
| 119 | libs = [ "UIKit.framework" ] |
| 120 | } |
| 121 | ``` |
| 122 | 2. Include your test target in the `deps` array of a suitable suite in |
| 123 | `//src/ios/chrome/test/earl_grey2/BUILD.gn`. |
| 124 | 3. Optional: If you feel like your new test should be in a new suite, or you |
| 125 | want to delete an existing suite to make tests better organized, you’ll need to |
| 126 | change the suites in `//src/ios/chrome/test/earl_grey2/BUILD.gn` in the format |
| 127 | of existing ones. (Do not forget to [config the bots] so the new suite can run |
| 128 | in infra.) |
| 129 | 4. Ensure your dependencies are correct. |
| 130 | ``` |
| 131 | $ gn gen --check out/Debug-iphonesimulator |
| 132 | ``` |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 133 | |
| 134 | ### Running EarlGrey tests |
| 135 | |
| 136 | EarlGrey tests are based on Apple's [XCUITest]. |
| 137 | |
| 138 | #### Running tests from Xcode |
| 139 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 140 | 1. If you added a new test file / suite, run `gclient runhooks` to sync for the |
| 141 | list of tests in Xcode. |
| 142 | 2. Change the scheme to "ios_chrome_eg2test". Create and select the simulator |
| 143 | you wish to use. |
| 144 | 3. You may run a test suite(module), TestCase or testMethod in test navigator. |
| 145 | Xcode will build the targets and run the test(s) you choose. Alternatively, |
| 146 | use ⌘+U to run all the tests. See Apple's [Running Tests and Viewing Results]. |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 147 | |
| 148 | #### Running from the command-line |
| 149 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 150 | EG2 tests can run in the command line with test runner scripts. You’ll need to |
| 151 | build the targets before running tests in cmd. This is used by continuous |
| 152 | integration infra and thus not user friendly. Running UI tests directly in Xcode |
| 153 | is recommended. |
| 154 | |
| 155 | Important notes: |
| 156 | * The test runner can invoke mac_toolchain to install a new Xcode of the version |
| 157 | specified to the path specified. You may want to choose a different path from |
| 158 | your daily use Xcode. |
| 159 | * If test_cases is empty in --args-json, all tests will run. Specifying a |
| 160 | testMethod to run is currently not supported in the test runner. |
| 161 | |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 162 | Example: |
| 163 | ``` |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 164 | src/ios/build/bots/scripts/run.py |
| 165 | --app |
| 166 | src/out/Debug-iphonesimulator/ios_chrome_ui_eg2tests_module-Runner.app |
| 167 | --host-app |
| 168 | src/out/Debug-iphonesimulator/ios_chrome_eg2tests.app |
| 169 | --args-json |
| 170 | {"test_args": [], "xctest": false, "test_cases": ["ReadingListTestCase"], |
| 171 | "restart": false, "xcode_parallelization": true, "xcodebuild_device_runner": |
| 172 | false} |
| 173 | --out-dir |
| 174 | path/to/output/dir |
| 175 | --retries |
| 176 | 3 |
| 177 | --shards |
| 178 | 1 |
| 179 | --xcode-build-version |
| 180 | 11c29 |
| 181 | --mac-toolchain-cmd |
| 182 | path/to/mac_toolchain |
| 183 | --xcode-path |
| 184 | path/to/Xcode.app |
| 185 | --wpr-tools-path |
| 186 | NO_PATH |
| 187 | --replay-path |
| 188 | NO_PATH |
| 189 | --iossim |
| 190 | src/out/Debug-iphonesimulator/iossim |
| 191 | --platform |
| 192 | iPad (6th generation) |
| 193 | --version |
| 194 | 13.3 |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 195 | ``` |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 196 | The invocation args are logged. You can find the latest arg format at the |
| 197 | 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] | 198 | |
| 199 | |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 200 | [config the bots]: https://chromium.googlesource.com/chromium/src/testing/+/refs/heads/master/buildbot/README.md#buildbot-testing-configuration-files |
| 201 | [Defining Test Cases and Test Methods]: https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods?language=objc |
| 202 | [EarlGrey]: https://github.com/google/EarlGrey/tree/earlgrey2 |
| 203 | [EarlGrey APIs]: https://github.com/google/EarlGrey/blob/master/docs/api.md |
| 204 | [eDO]: https://github.com/google/eDistantObject |
| 205 | [Example of App Interface]: https://cs.chromium.org/chromium/src/ios/chrome/browser/metrics/metrics_app_interface.h |
| 206 | [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] | 207 | [instructions]: ./build_instructions.md |
Zhaoyang Li | 27002046 | 2020-10-27 23:07:29 | [diff] [blame] | 208 | [Running Tests and Viewing Results]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/05-running_tests.html |
| 209 | [this AppLaunchManager API]: https://source.chromium.org/chromium/chromium/src/+/master:ios/testing/earl_grey/app_launch_manager.h;drc=d0889865de20c5b3bc59d58674eb2dcc02dd2269;l=47 |
Mike Baxley | 47db7d8 | 2017-11-16 15:57:17 | [diff] [blame] | 210 | [XCUITest]: https://developer.apple.com/documentation/xctest |