
- Make run.py's docstring match what currently works (i.e. no xcodebuild/ intermediate directory, pass --iossim). - In the main testing.md document, point to the run_<foo> wrapper script and given an example. The previous instructions from 2017 said one just needed to run the unit tests directly, which is not true. Change-Id: I8babef7dc0ed87b316b22bba8fb7b4ae6a037dd3 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6185886 Commit-Queue: Yue She <yueshe@google.com> Auto-Submit: Raphael Kubo da Costa <kubo@igalia.com> Reviewed-by: Yue She <yueshe@google.com> Cr-Commit-Position: refs/heads/main@{#1408985}
235 lines
9.1 KiB
Markdown
235 lines
9.1 KiB
Markdown
# Automated testing for Chrome for iOS
|
||
|
||
See the [instructions] for how to check out and build Chromium for iOS.
|
||
|
||
Automated testing is a crucial part of ensuring the quality of Chromium.
|
||
|
||
## Unit testing
|
||
|
||
Unit testing is done via gtests. The easiest way to run a unit test is to use
|
||
the wrapper scripts generated at build time:
|
||
|
||
```sh
|
||
<output directory>/bin/run_<target_name> [options]
|
||
```
|
||
|
||
The wrapper scripts take care of invoking `//ios/build/bots/scripts/run.py`
|
||
with options like `--iossim` set appropriately. In general, you need to pass
|
||
at least `--out-dir` (a directory where test results will be stored),
|
||
`--platform` (a device available to the simulator), `--xcode-build` (obtained
|
||
via e.g. About XCode) and `--version` (iOS version to run). The
|
||
`--gtest_filter` option is also supported.
|
||
|
||
A more complete example looks like this:
|
||
|
||
```sh
|
||
out/Debug-iphonesimulator/run_base_unittests \
|
||
--gtest_filter=Base64Test.Basic \
|
||
--platform "iPhone 16" \
|
||
--version 18.2 \
|
||
--xcode-build 16c5032a
|
||
```
|
||
|
||
## Integration testing
|
||
|
||
[EarlGrey] (EG2) is the integration testing framework used by Chromium for iOS.
|
||
|
||
### Writing EarlGrey tests
|
||
|
||
#### Before you start
|
||
|
||
* Just write a unit test if the purpose of your test does not involve UI.
|
||
* Learn about EarlGrey test framework principles and APIs in [EarlGrey].
|
||
* Learn about [Defining Test Cases and Test Methods] from Apple.
|
||
|
||
#### Creating test files and writing EG2 tests
|
||
|
||
1. EG2 test files are ended with _egtest.mm, and usually located within the same
|
||
directory of the UI code you wish to test.
|
||
2. Basic imports of a EG2 test file:
|
||
|
||
* You’ll have to include:
|
||
```
|
||
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
|
||
```
|
||
* You’ll most likely find util functions in these files helpful.
|
||
```
|
||
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
|
||
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
|
||
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
|
||
```
|
||
* Beside these, directly import an EG2 header for an EG2 API you are using.
|
||
|
||
3. TestCase/testMethods definitions. Create `SomeGreatTestCase` as a subclass of
|
||
`ChromeTestCase`. Create test methods, eg `-(void)testMyGreatUIFeature {...}`,
|
||
and put UI actions within the test methods.
|
||
* Put your setup and tear down code for the *TestCase* in
|
||
`+(void)setUpForTestCase` and `+tearDown`. These will run once before and
|
||
after all tests for the test class.
|
||
* Put your setup and tear down code for each *test method* in `-(void)setUp`
|
||
and `-(void)tearDown`. These will run before and after every
|
||
`-(void)testMethod` in the file.
|
||
4. Writing test contents. See the chrome helpers (imports in 2.) as well as
|
||
[EarlGrey APIs] to write a UI action/assertion in your testMethod.
|
||
|
||
#### Interacting with the app in a test
|
||
|
||
##### Relaunch app with different flags
|
||
|
||
In EG2 tests, the test process launches the host app process at the beginning,
|
||
then runs UI actions/assertions in the app. To pass args or feature flags to the
|
||
app at initial launching, or relaunch the app in the middle of your test, see
|
||
[this AppLaunchManager API].
|
||
|
||
##### Accessing app internals
|
||
|
||
EG2 test targets are built with test-related code but without app code.
|
||
|
||
To access anything from the app side, use an "app interface". App interface is
|
||
implemented as a class that lives in the app process, but can be accessed in the
|
||
test process through [eDO]. You can include the header in your test side code
|
||
and call class methods of the interface class. The methods will execute code in
|
||
the app process and can return basic Objective-C types. See this [Example of App
|
||
Interface].
|
||
|
||
See `eg_test_support+eg2` (test side utilities) and `eg_app_support+eg2` (app
|
||
side utilities) targets in `BUILD.gn` files to learn how test utilities are
|
||
organized in targets. If you added an app side helper (app interface), you’ll
|
||
also need to include your new `eg_app_support+eg2` target in
|
||
`//ios/chrome/test/earl_grey/BUILD.gn`’s `eg_app_support+eg2` target. ([Example
|
||
CL adding App Interface]).
|
||
|
||
Note that if you create an App interface, you can’t build the app interface
|
||
class in your eg2_tests target, but you need to include and refer to it. To
|
||
satisfy the linker, you'll need to create a `my_test_app_interface_stub.mm`
|
||
file with the following content in it and build it as a dependency of your
|
||
tests that use the app interface.
|
||
|
||
```objc
|
||
#import "ios_internal/chrome/test/earl_grey2/my_test_app_interface.h"
|
||
|
||
#import "ios/testing/earl_grey/earl_grey_test.h"
|
||
|
||
GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(MyTestAppInterface)
|
||
|
||
```
|
||
|
||
If you don't you'll get linker errors that read like “Undefined symbols for
|
||
architecture… MyTestAppInterface”
|
||
|
||
#### Creating test targets and adding the target to test suites
|
||
|
||
1. Create a test target. Add a target(`source_set`) named "eg2_tests" into the
|
||
closest `BUILD.gn` file. Put the test file into the `sources` array and put the
|
||
targets containing headers used in your test file into `deps` array. This is to
|
||
organize test source files and dependencies so that the GN build system can
|
||
correctly build the test module. The skeleton of the target:
|
||
```
|
||
source_set("eg2_tests") {
|
||
configs += [
|
||
"//build/config/ios:xctest_config",
|
||
]
|
||
testonly = true
|
||
sources = [
|
||
"some_egtest.mm"
|
||
]
|
||
deps = [
|
||
"//ios/chrome/test/earl_grey:eg_test_support+eg2",
|
||
"//ios/testing/earl_grey:eg_test_support+eg2",
|
||
]
|
||
frameworks = [ "UIKit.framework" ]
|
||
}
|
||
```
|
||
2. Include your test target in the `deps` array of a suitable suite in
|
||
`//src/ios/chrome/test/earl_grey2/BUILD.gn`.
|
||
3. Optional: If you feel like your new test should be in a new suite, or you
|
||
want to delete an existing suite to make tests better organized, you’ll need to
|
||
change the suites in `//src/ios/chrome/test/earl_grey2/BUILD.gn` in the format
|
||
of existing ones. (Do not forget to [config the bots] so the new suite can run
|
||
in infra.)
|
||
4. Ensure your dependencies are correct.
|
||
```
|
||
$ gn gen --check out/Debug-iphonesimulator
|
||
```
|
||
|
||
### Running EarlGrey tests
|
||
|
||
EarlGrey tests are based on Apple's [XCUITest].
|
||
|
||
#### Running tests from Xcode
|
||
|
||
1. If you added a new test file / suite, run `gclient runhooks` to sync for the
|
||
list of tests in Xcode.
|
||
2. Run a test suite(module), TestCase or testMethod in test navigator.
|
||
Xcode will build the targets and run the test(s) you choose. Alternatively,
|
||
use ⌘+U to run all the tests. See Apple's [Running Tests and Viewing Results].
|
||
3. You can pass extra arguments to the app process with `--extra-app-args`, e.g.
|
||
`--extra-app-args='--enable-features=Foo'`.
|
||
* This might not work consistently as tests can re-launch the app with
|
||
arbitrary command-line arguments.
|
||
|
||
|
||
#### Running from the command-line
|
||
|
||
EG2 tests can run in the command line with test runner scripts. You’ll need to
|
||
build the targets before running tests in cmd. This is used by continuous
|
||
integration infra and thus not user friendly. Running UI tests directly in Xcode
|
||
is recommended.
|
||
|
||
Important notes:
|
||
* The test runner can invoke mac_toolchain to install a new Xcode of the version
|
||
specified to the path specified. You may want to choose a different path from
|
||
your daily use Xcode.
|
||
* If test_cases is empty in --args-json, all tests will run. Specifying a
|
||
testMethod to run is currently not supported in the test runner.
|
||
|
||
Example:
|
||
```
|
||
src/ios/build/bots/scripts/run.py
|
||
--app
|
||
src/out/Debug-iphonesimulator/ios_chrome_ui_eg2tests_module-Runner.app
|
||
--host-app
|
||
src/out/Debug-iphonesimulator/ios_chrome_eg2tests.app
|
||
--args-json
|
||
{"test_args": [], "xctest": false, "test_cases": ["ReadingListTestCase"],
|
||
"restart": false, "xcode_parallelization": true, "xcodebuild_device_runner":
|
||
false}
|
||
--out-dir
|
||
path/to/output/dir
|
||
--retries
|
||
3
|
||
--shards
|
||
1
|
||
--xcode-build-version
|
||
11c29
|
||
--mac-toolchain-cmd
|
||
path/to/mac_toolchain
|
||
--xcode-path
|
||
path/to/Xcode.app
|
||
--wpr-tools-path
|
||
NO_PATH
|
||
--replay-path
|
||
NO_PATH
|
||
--iossim
|
||
src/out/Debug-iphonesimulator/iossim
|
||
--platform
|
||
iPad (6th generation)
|
||
--version
|
||
13.3
|
||
```
|
||
The invocation args are logged. You can find the latest arg format at the
|
||
beginning of stdout from an infra test shard if the above doesn't work.
|
||
|
||
|
||
[config the bots]: https://chromium.googlesource.com/chromium/src/testing/+/refs/heads/main/buildbot/README.md#buildbot-testing-configuration-files
|
||
[Defining Test Cases and Test Methods]: https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods?language=objc
|
||
[EarlGrey]: https://github.com/google/EarlGrey/tree/earlgrey2
|
||
[EarlGrey APIs]: https://github.com/google/EarlGrey/blob/master/docs/api.md
|
||
[eDO]: https://github.com/google/eDistantObject
|
||
[Example of App Interface]: https://cs.chromium.org/chromium/src/ios/chrome/browser/metrics/model/metrics_app_interface.h
|
||
[Example CL adding App Interface]: https://chromium-review.googlesource.com/c/chromium/src/+/1919147
|
||
[instructions]: ./build_instructions.md
|
||
[Running Tests and Viewing Results]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/05-running_tests.html
|
||
[this AppLaunchManager API]: https://source.chromium.org/chromium/chromium/src/+/main:ios/testing/earl_grey/app_launch_manager.h;drc=d0889865de20c5b3bc59d58674eb2dcc02dd2269;l=47
|
||
[XCUITest]: https://developer.apple.com/documentation/xctest
|