Microsoftは、MSTestとMicrosoft.Testing.Platformを使用してUWPおよびWinUI 3アプリをテストするための実践的なガイドを公開しました。このガイドでは、MSIX形式でパッケージ化されたアプリ、非パッケージ化アプリ、AppContainer内で動作するアプリなど、異なる実行モデル間でテストのライフサイクルを統一します。この記事は主任ソフトウェアエンジニアのAmaury Levéによって執筆され、UIテスト自体の記述よりも、テストホストの実行方法を決定する詳細に重点を置いています。
実行モデルの選択
Microsoftは、パッケージ化と信頼レベルを区別しています。パッケージ化によってMSIX IDとAUMIDによるアクティベーション手段が追加され、信頼レベルによってプロセスが完全な権限で実行されるか、AppContainer内で実行されるかが決まります。パッケージID、パッケージ化されたアクティベーション契約、またはインストール済みアプリの動作との一致が必要でない場合、ガイドでは、パッケージ化されていないWinUI 3アプリから始めることを推奨しています。一方、UWPは本質的にパッケージ化およびAppContainer環境と結び付いています。
パッケージ化されていないアプリでは、Microsoft.Testing.Platformはapphostから直接実行するパスを使用します。一方、パッケージ化されたアプリでは、パッケージレイアウトの登録とAUMIDによるアクティベーションが必要です。また、AppContainer内で動作するよう構成されたWinUI 3アプリでは、コンソール、キャンセル、TRXファイル、HangDump、再試行用の通信チャネルへのアクセスを正確に委任する必要があります。Microsoftは、その代わりにALL APPLICATION PACKAGES権限を付与することに注意を促しています。
MSTestとMicrosoft.Testing.Platformの設定
Microsoftは、MSTest.Sdk 4.5をインストールし、global.jsonファイルでMicrosoft.Testing.Platformを選択することを推奨しています。これにより、dotnet testコマンドはVSTestではなく、.NET 10のネイティブテストプラットフォームを使用します。MSTest.Sdk 4.5には、MTP 2.5、アプリケーションモデル専用のサイドカーコンソール、必要なUWPアセット、パッケージ化されたアプリランナーが含まれています。
最新のUWPでは、適切なWindowsターゲットフレームワークとともに、UseUwpおよびPublishAotを使用する必要があります。アプリは、OnLaunched内から生成されたヘルパーMicrosoftTestingPlatformApplication.RunAsyncに、PackagedAppExtensions.GetTestApplicationArgumentsを使用してアクティベーション引数を渡す必要があります。従来のUWPプロジェクトでは既存の構造を維持し、MSBuild.Sdk.ExtrasとともにMSTest.Sdkをインポートしてから、Developer PowerShellでInvokeTestingPlatformターゲットを使用してテストを実行します。
WinUI 3ホストとUIディスパッチャー
この記事では、自己ホスト型のWinUI 3テストアプリを構築する方法を提案しています。このアプリは自身のウィンドウを作成し、エントリーポイントを所有したうえで、同じプロセス内でMSTestを実行します。ウィンドウをアクティブ化した後、アプリはDispatcherQueueオブジェクトをUITestMethodAttribute.DispatcherQueueに公開し、その後RunAsyncを呼び出します。WinUIが生成するエントリーポイントはvoidを返すため、Environment.ExitCodeをテスト実行の結果に設定する必要があります。これを無視すると、ビルドシステムまたはCI上で失敗した実行が成功として表示される可能性があります。
サンプルでは、UITestMethodがTestInitializeおよびTestCleanupを含むテスト全体をWinUIディスパッチャーに渡します。一方、STATestMethodはSTAスレッドを提供しますが、それ自体ではWinUIディスパッチャーを作成しません。そのため、DispatcherQueueに依存するUIテストには単独では不十分です。
実際に何が変わるのか
このアプローチの主な価値は、UWPとWinUI 3の間、またはパッケージ化と非パッケージ化された実行の間を移行する際に、MSTestと同じテストライフサイクルを維持し、変更をデプロイ設定と起動パスだけに限定できることです。パッケージ化されていないWinUI 3では、WindowsPackageTypeをNoneに設定し、MSIX toolingを無効にします。パッケージ化された実行ではPackage.appxmanifestとパッケージアセットを保持し、10.0.19041.0以降のWindows TFMと、Developer Modeなどのサイドローディングを許可するポリシーが必要です。
両方のモデルは、.NET 10ではdotnet runまたはdotnet test --projectを使用して実行できます。一方、AppContainer内でUWPおよびWinUI 3のテストを実行する場合は、専用のMSBuildターゲットを使用し、昇格されていないDeveloper PowerShellから実行します。Microsoftは、パッケージ化されていないアプリでdotnet execを使用しないよう警告しています。実行パスにdotnet.exeを挿入すると、WinUIリソースの読み込みに問題が発生する可能性があるためです。
CI内での検証
パッケージ化されたテストが有効であることを確認するには、ビルドが成功するだけでは不十分です。実際のCIエージェントイメージ、パッケージを登録するユーザーコンテキスト、Developer Modeまたはサイドローディングのポリシー、パッケージが宣言するフレームワークを確認する必要があります。Windows App SDKまたはUWPパッケージがあらかじめインストールされた開発者マシンでは、クリーンなエージェントで初めて明らかになる差異が隠れる可能性があります。また、Windows App SDKフレームワークに依存するアプリには、self-contained形式でビルドされていない限り、対応するランタイムのバージョンが必要です。その場合でも、CIで使用されるものと同じパッケージモデルをテストする必要があります。