XCUITestをダークモードで実行する
iOSの場合、Appの起動前にデバイスに対して設定します。
(起動後でもよいです。その場合、設定したタイミングで変わります)
func testSomethingWithDarkMode() throws{
XCUIDevice.shared.appearance = .dark // .lightでライトモードになります。
let app = XCUIApplication()
app.launch()
// test something
}
launchArguments
を探したりしたのですが(実際macOSには、-AppleInterfaceStyle Dark
、という設定がありそうです)、iOSの場合は上記のようにすればよいようです。
Xcode 14.3、iOS 16.4(シミュレータ)です。
同様に、向きを変えるなら
XCUIDevice.shared.orientation = .landscapeLeft
のように指定します。
ただ、シミュレータの中の表示の中身の向きが変わるだけで、シミュレータ全体は縦のままです。
自動テスト上は問題ないのですが、実行しているさまを眺めるのには首(顔?)をかしげる必要があります。
(事前にシミュレータ自体をランドスケープにしておけば、かしげずに確認できます)
シミュレータ全体の向きをテスト実行中に変える方法は分かりませんでした。
AppStore用のスクリーンショット撮るとなると、同じ画面を日本語・英語などの対応言語数×用意する画面サイズ数、って感じで撮らなくてはなりません。
英語・日本語、iPhone 14 Pro/iPhone 8 Plus/iPad Pro 6th (+iPad Pro 2nd)ってなると、同じ画面やシーンの設定違いを6枚(or 8枚)撮ります。基本的にはその6枚は、同じ設定で同じ操作をした状態にしたいところです。
設定と操作はXCUITestでできるし、そのままスクリーンショットも撮れるから、XCUITest使えばいいじゃん、ってなってます。
で、ダークモードを切り替えたり、向きを変えたりまでは分かりました。
あとは撮ったスクリーンショットに自動で名前をつけて保存したいところですが、その際、上記の6種類を識別できるように命名したい。なるべくテストケース側にそういうのを書くのではなく、テストケース側で勝手にシミュレータの情報を判断して、「(画面名/シーン名)-(iPhone/iPad)-(世代)-(Light/Dark)-(Landscape/Portrait).png」とかって付けて欲しい。
iPhone/iPadの区別はシミュレータでのXCUITest実行時でもテストケース内でUIDevice.current.model
を参照すれば取れることが分かりましたが、iPhone 14 ProかiPhone 8 Plusかが分からない。画面サイズとしてUIScreen.main.nativeBounds.size
をとっても同じ値(960×1440)が返ってきてしまいます。
あるいはiPad Pro 6thか2ndかが分からない。そもそもこの2つは画面サイズも同じ。
ここまで書いて思いましたが、ハードウェアボタンの有無、ということで分けとけば良い気がしました。
また通常のUITestとはターゲット(UITest Bundle)を分けて管理すると良さそうです。テストはテスト用、スクリーンショットはスクリーンショット用のターゲットに分ける。
テストはやはりすべて一括実行とかしたいですし。
なんでだか、XcodeからiPhone 8/iPhone 8 PlusのXCUITestが実行できません。Building <ターゲット名>...から進まない。
コマンドラインツールであるxcodebuild
からはビルド、テスト実行ができます。
iPhone 14 ProとかiPad Pro 6th genとかはXcodeからXCUITestを実行できます。
XCUITestでない普通のデバッグ実行はiPhone 8/8 Plusでもできます。
環境はmacOS 13.3.1/Xcode 14.3/iOS 16.4 (simulator)/M1 Mac Book Proです。
ここまで書いて思いましたが、ハードウェアボタンの有無、ということで分けとけば良い気がしました。
ほんとかよ、という気がしますが、XCUIDevice.shared.hasHardwareButton(.home)
は、シミュレータをiPhone 14 Pro Maxで実行するとtrue
、iPhone 8 Plusで実行するとfalse
を返します。
マジ?
あと、XCUIDevice.shared.appearance
とXCUIDevice.shared.orientation
はenumなので、そのままファイル名に使えないかなということでString(describing: XCUIDevice.shared.appearance)
とやりましたが、ダメですね。
"XCUIDeviceAppearance(rawValue: 0)"
みたいな文字列が返ってきてしまいます。
普通のenumの場合は、enumのcase名がそのまま文字列で返ってきます。
@objcというのが指定されてるenumはダメ、というのは色々なところに書いてあるのを見ます。
今回のは、XCUIDevice
自体がNSObject
の派生クラスで、そのなかにAppearance
というenumが定義されています。直接は@objcというのは指定されてないように見えます。
とはいえ、包含クラスは明らかにObjective-Cのクラスなので、それが原因かも知れません。詳しいルールはよく分かりません。
コードで比較して、.darkなら"Dark"とかつって、愚直にやります。