Xcode10から最新(Xcode14)までのTestingをふりかえる - 高速化編 -
はじめに
2022/10/05に「Xcode10から最新(Xcode14)までのTestingをふりかえる」というタイトルで登壇しました。
次は登壇時の資料です。
30分という登壇時間はあったもののボリュームがあったので細かいところまで話せていないところが多いです。
せっかくなので、内容の補足を本記事でしたいと思います。
ただ、全てについて補足すると記事のボリュームが凄くなると思うので、今回は「高速化」についての話をしたいと思います。
高速化の必要性
iOSにおける自動テストを実行する上で時間がかかる場所はどこでしょう?
たとえば次のような箇所があります。
ビルド
シミュレーター上でのテストの実行(シミュレーターの用意)
テスト自体の実行時間の長さ
さらに、次のように実行したい環境が増えているという側面もあります。
多言語対応
複数のデバイスタイプ
Xcode11から「Test Plans」の登場により、自動テストを実行したい環境を設定することは以前より楽になりました。
しかし、「(実行したい)環境の多さ x 実行時間の長さ」だと実時間にテストを終わらせるというのはなかなか厳しく、実行したいタイミングはあるもののテストの実行頻度を少なくする、または実行しないという選択をしているケースもあるかもしれません。
テストの実行時間に長い時間かかると「自動テストの終了」を待たないというケースも起きます。
GitHubでテストがpassすることをPRのmerge条件にする際の足かせにもなりえます。
これらを解決するためにも、テストの実行時間を短くする行為は重要です。
それとは別に常にすべての自動テストを実行するのではなく、どのタイミングでどのような自動テストを実行するかの判断は重要ですし、変更された内容によって実行する自動テストを絞るというのも良い方法です。
高速化の歴史
高速化をおこなうには、ビルドマシンの性能を上げるという行為はテストやビルドの実行時間を早くするという点においては重要です。
それ以外にAppleが提供している高速化に寄与する機能を活用するということも重要です。
今までにどういった機能が提供されてきたかというとざっくりと説明すると次のような感じです。
ビルドとテストの分離(Xcode 8)
ビルドコマンド(xcodebuild)による並列化(Xcode 9)
Xcode上での並列実行(Xcode 10)
実機を利用した並列実行(Xcode 11.4)
再インストール無しによるUIテストの高速化(Xcode 11.4)
ターゲットも含めた並列実行(Xcode 14)
これらについて軽く次に説明をしていきます。
ビルドとテストの分離と並列化
Xcode 8の段階で「ビルドとテストの分離」ができるようになっただけでなく、「特定のテストの実行(-only-testingオプション)」や「特定のテストのskip(-skip-testingオプション)」が可能になりました。
高速化の基本は並列実行です。
この機能により自動テストの並列実行が可能になりました。
ただ、可能であるとはいえ並列実行をおこなうのはめんどうでした。
それをラクにしてくれるOSSも登場しました(後述します)。
その後の機能提供によりかんたんに並列化ができるようになってきました。
今では、Xcodeでオプションを1つチェックするだけで同一環境(指定したiOSシミュレーターをcloneして実行環境が複数用意される)でテストを並列実行することができるようになりました。
これらによりテストの並列実行は、かんたんになり以前と比べるとテストの実行時間を削減することが可能になりました。
とはいえ、実行する環境のスペックによって並列数の上限も変わってくる点は注意が必要です。
マルチモジュール化による変化
最近では「マルチモジュール化」をおこなうケースもあります。
その場合においてはモジュール単位でテストを実行することによる並列化だけで十分なケースもあります。
上述したテストの並列化は「テストの実行時間」が長い場合において有効であり「テストの実行時間」が短いもの x n個の環境であれば、そこまで有効にはならないかもしれません。
とはいえ全てにおいてそうではなく、複数の環境で実行する必要がある場合においては、活用するメリットがあります。
取り巻く環境
Appleが提供する機能で足りなかったり、またより使い勝手をよくするために、独自のOSSやサービスが開発されることはよくあります。
高速化に関係するものとしては次のようなものがあります。
OSS
OSSでは次のようなものがあります。
どちらも並列実行をかんたんにサポートしてくれるものです。
Bluepill(2017/01 - )
BluepillはUIテストを複数のシミュレーターで並列実行してくれます。
Flank(2018/03 -)
Flankは後述するデバイスファームである「Firebase Test Lab」を使って自動テストを並列実行するのをサポートしてくれます。
なお、Appleによる変化に伴いOSS側も追従が必要だったり、必要がなくなったりといったケースも起きます。
デバイスファーム(iOSサポート)
実行環境(iOSシミュレーター、実機)は自動テストにおいては重要です。
この環境(特に実機)を自身で複数用意するのは非常に面倒です。
それをサポートしてくれるものとして、デバイスファームというものがあります。
iOSの実機がサポートされた時期で見るとAndroidより遅くなりますが、次の時期には提供がはじまっていました。
AWS Device Farm(2015/12 -)
Firebase Test Lab(2018/12 -)
これらは自動テストに必要なファイル(複数)をzip化してアップロードすることで指定した環境でテストを実行してくれます。
この複数のファイルは自前で集めてzip化してアップする必要がありましたが、最近では1つにまとめてくれるような機能をAppleが提供してくれたため、そのうちここらへんもラクになるかもしれません。
参考:xctestproductバンドル
これらと並列化を組み合わせることで、実行時間の短縮や実行環境の多様化が可能になっています。
外部サービス
便利になったとはいえ自動テストを自前で実装し、実行環境も自分で用意するというのはいささか面倒なのは事実です。
そこで、一括でサポートしてくれるサービスも世の中には出ています。
これらのサービスはモバイルアプリのE2Eテストのサポートをしてくれるものです。
テストケースの作成とそのテストの実行までサポートしてくれます。
MagicPod(2017/07 -)
Waldo(2018 -)
Autify for Mobile(2021/04 -)
これらを利用する場合においても、ソフトウェアテストや自動テストの知識が一切なくていいかと言われるとそうではなく、知識があることでより活用できるのは事実です。
テストケースの作成が容易になったからといって、闇雲に作れば運用が大変になりますし、アプリケーション側に工夫を入れなければテストの実行時間が長くなりがちだったり、安定性のわるいテストにもなりえます。
並列化をする際の注意点
今までに高速化の必要性や歴史や取り巻く環境について話してきました。
実際に高速化でよくおこなう並列化をしていく上では、注意するべき点もあります。
いろいろとありますが2つほど次に紹介したいと思います。
ここらへんの話は既存のTesting Frameworkでも同様なことが多かったりします。
そういう意味では、他についても知っておくとよりわかりやすくなるかもしれません。
1つのクラスの実行時間を長くしすぎない
並列化を自動でおこなう場合、下図の左側のように終わった順に次のクラスがアサインされていきます。
Aのように実行時間が長いクラスがあると、自動テストの終了時間はAクラスの実行時間に依存してしまいます。
そこでAクラスをA1とA2に分割することで、実行時間の最適化をおこなうのが望ましいです。
なお、順次アサインされるので各クラスの実行時間に応じた最適化がおこなわれるわけではありません。
それについては上述したFlankというOSSにおいては、前回の実行時間のデータを元に最適化をおこなうというアプローチを採用したりしています。
他のテストに依存しないようにする
よくある話かもしれませんが、並列化を意識していないとあるテストケースが他のテストケースに依存した形になってしまうことがあります。
そうすると、並列化で実行順序が異なった結果、テストが落ちるということが起きたりします。
そこで、テストの実行順序をランダムにするというアプローチがあります。
注意点としては、ランダムでは実行してくれるものの同じ実行順で実行するためのseedは発行されません。
そのため、仮に1度落ちたときと同じ実行順序で再度テストを動かすというのがむずかしいというのがあります。
さいごに
高速化にフォーカスして、ある程度まとめてみました。
ここらへんのアプローチの仕方は、Android側と比較するとその歴史の推移や差が見えて非常に面白いと思います。
似ているところや違うところがあります(shardの仕方とかテストのアサインのさせ方とか)。
自動テストを活用して、そこで得られたナレッジをアウトプットしてもらえると私も嬉しいです。
この記事が気に入ったらサポートをしてみませんか?