この記事は「Selenium/Appium Advent Calendar 2019」1日目の記事です。今回は、Appiumの開発をしている中で問題の報告が多かったXcode 11、iOS 13における実行性能の劣化問題を調べていた時に知ったことです。内容はこのissueの一部です。
Appiumの実行形態とその実行性能
Appiumは基本的には中間サーバとしてテストケースとテスト対象の操作を仲介します。
ある利用者がテストケースを特定の開発言語なりで記述、実行するとします。そのテストケース実行系の中でAppiumサーバに対してHTTPリクエストを送出し、リクエストを受け取ったAppiumサーバーは自身が管理する実行端末上で動作するサーバーに対して操作を要求したり、自身で端末を直接操作します。その操作結果をテストケース実行系に対して返信します。この一連の流れが1つのセットとなり、Appiumのもつ個々の操作が行われます。

つまり、1つのAppiumのコマンド実行時間には、HTTP通信の往復時間が必ずオーバーヘッドとして存在することになります。実行系全体が同一端末に存在する場合、その時間はごく僅かであることが多いです。一方、物理的に異なる環境でテストケースの実行元と実行環境が離れている場合、そのHTTPリクエストにかかる時間は秒単位で大きくなる可能性もあります。(AppiumProにある例)
iOSの実行系とXcode 11の変化
現在、AppiumにおけるiOSのテストとしてXCUITest Driverと表現する実行系があります。Appiumは中間サーバとして機能すると先ほど述べました。つまり、Appium自体はその実行端末を操作する側の実装があれば、様々な端末を対象に同様のコマンド実行を実現できます。iOSではXCUITest Driverが、AndroidではUIAutomator2やEspresso Driver、WindowではWinAppDriverなど存在します。
この中で、今回の話題の対象はXCUITest Driverです。この仕組みとして、WebDriverAgentと呼ぶAppleが提供するXCTest(UI)や周辺APIをラップする、iOS端末上で動作するサーバがあります。AppiumサーバはこのWebDriverAgentと独自のiOS端末との直接操作する手段を介してテスト対象となるアプリなどを操作します。
WebDriverAgentはXcode 9の頃から同様の機能を実現していました。Xcodeも11となり、XCTestが提供するUIテストむけ機能も安定、拡充してきました。古いXCTest(UI)などが提供するプライベートAPIもDeprecatedになり、Xcode 11ではXCTest(UI)が内部で利用しているであろうメソッドを利用することにしました。
Xcode 11とiOS 13環境下ではiOS 12環境では問題ない操作が遅くなるなどの報告が出るようになりました。この問題が性能計測の発端です。目的としては、Appiumが利用しているメソッドが問題なのか、XCTest(UI)自体がXcode 11とiOS 13環境において問題を抱えているかでした。
AppiumとXCTest(UI)との実行差分
原理的には、XCUITest DriverとXCTest(UI)は、実際にアプリを操作するためのメソッドは同一です(であるはず)。となると、その実行時間の差分は、AppiumがHTTPリクエストを介するぶんだけ遅延することが常であるはずです。
先に計測結果だけ述べます。
Xcode 11環境下において、iOS 13を対象にするとほぼ期待通りの結果であるいっぽう、iOS 12を対象とするとAppiumの方が高速であることがわかりました。少し前に記述した「Xcode 11ではXCTest(UI)が内部で利用しているであろうメソッドを利用することにしました。」と説明した箇所のメソッドの利用は、内部的には、selectorと呼ばれるあるメソッドが存在するとそれを利用するというロジックを利用しています。そのメソッドは実際にはXcode 9以降に存在するものであるため、実質的にはAppiumはiOS 12、13問わずそのメソッドを利用します。iOS 13の実行性能がAppiumとXCTest(UI)では大差ないこと、XTest(UI)はiOS 12と13では実行性能はほぼ同一なので、XCTest(UI)はiOS 12、13に関わらず同一のメソッドを経由して要素検索などを実行している可能性が高いです。
と考えると、AppiumのiOS 12以下に対する操作がiOS 13はもとより、XCTest(UI)より高速であることは驚きです。内部実装の詳細は知るすべがないのでこれ以上の言及はできませんが、iOS 13は実行性能がiOS 12よりは劣化している面はありつつも、AppiumのHTTPリクエストを除く実行遅延はXCTest(UI)とほぼ同様であることがわかります。Appiumが遅かった場合、XCTest(UI)でも遅い可能性があり、Appleさんの改善に期待したいところで。
計測値
ともあれ、行なった計測を提示します。
テスト対象

テスト対象は、新規作成したプロジェクトにButton要素を付与しただけのアプリです。至極単純なアプリです。
- Appium
- 1.16-beta
- Xcode
- 11.2.1
- Swift
- 5.1
- Ruby
- 2.6.2
テスト操作
操作は簡単な、「検索した要素に対してタップする」操作です
テストコード
テストコードは以下です。結果の集計周りは省略しています。RubyクライアントではBenchmarkを、iOSではXCTPerformanceMetricを利用して数値を計測します。最近のXcodeは要素が1つの場合はfirstMatchが機能するので、計測を公平にするためにAppiumでもuseFirstMatchをtrueにして計測しています。(今回の結果では、このuseFirstMatchの有無は結果に影響しませんでしたが。)
// Ruby
puts Benchmark.measure {
@driver.find_element(:accessibility_id, 'Button' ).click
}
// Swift
func testMeasurement() {
measureMetrics([XCTPerformanceMetric.wallClockTime], automaticallyStartMeasuring: true, for: {
XCUIApplication().buttons["Button"].tap()
})
}
結果
あまりひねりのない内容であるので、結果を載せます。試行回数は10回です。多少の揺らぎも存在しますが、だいたいこの通りです。
| Appium (秒) | XCTest (秒) | |
|---|---|---|
| iOS 13.2.2, iPhone 8 simulator | 0.62 | 0.61 |
| iOS 12.4, iPhone 8 simulator | 0.41 | 0.60 |
| iOS 13.2.2, iPhone SE (実機) | 0.81 | 0.75 |
これを見ると、予想通り、少なくともHTTPリクエストぶんほどのオーバーヘッドがAppium側には存在することがわかります。一方で、確かにiOS 12の時にXCTest(UI)よりのAppiumの方が良い性能を出していることもわかります。
実機ではAppium、XCTestも共に実機ではシミュレーターより遅いことがわかります。私がiOS 12の実機を持たないためiOS 12環境では比較できてはいません。
まとめ
Appiumと純粋なXCTest(UI)との実行性能比較を、非常に簡単なアプリを対象に行いました。結果、ローカル環境で実行する範囲ではさほど差はないことがわかりました。iOS 13でも常には遅延は発生せず、何らかの条件があるであろうことはわかりました。
何らかの条件は、例えば現在はWebViewが絡むものはXCTest(UI)のレイヤーで遅くなることが観測されています。これは、iOS 13でDeprecatedとなっているUIWebViewを利用する環境下で発生するようです。この場合、WKWebViewを利用するように製品コード側を修正する必要があります。
蛇足ですが、Appiumの初期準備動作が遅いことが上記の実行遅延の他に指摘されることがあります。例えばこういうAppiumで提供されるXCTestの仕組みを利用した高速化対応を利用することで数秒以内に落とし込むことも可能です。
それでは、Happy Testing!