tafuji's blog

C#, Xamarin, Azure DevOps を中心に書いています。

Xamarin.Essentials メモ:#2 単体テスト

はじめに

Xamarin.Essentials のプラットフォーム固有機能のテストはどうやって行われているのかを調べた時のメモです。

Xamarin.Essentials プラットフォーム依存部分のテスト

プラットフォーム固有部分のテストコードは、DeviceTests 配下にまとめられています。 その他わかったことは以下の点です。

  • テストフレームワークは xUnit を利用している
  • テストコードは共通の DeviceTests.Share にまとめられている
  • それぞれのプラットフォームのテストプロジェクトで、DeviceTests.Share のプロジェクトを参照している
  • テストの初期化時に、tests.cfg ファイルを読み込む処理があるが、リポジトリ上ではこのファイルは空である
    • 後述の build.cake ファイルのスクリプトの処理で、tests.cfg ファイルにポート番号などが書き込まれていることが分かった(iOS の場合の処理
  • テストの実行は、自動で行われているようで、テストスクリプトは、build.cake ファイルに書かれている

build.cake ファイル

このファイルは、Cake (C# Make) という C#スクリプトを書くことができるビルド自動化用のツールで利用するファイルです。

どうやって自動ビルド(自動単体テスト)をやっているのか

iOS の場合の Task("test-ios-emu") をざっと読んでみる。

最初は単体テストに利用する Simulator を検索する

var sims = ListAppleSimulators ();
foreach (var s in sims)
{
    Information("Info: {0} ({1} - {2} - {3})", s.Name, s.Runtime, s.UDID, s.Availability);
}

// Look for a matching simulator on the system
var sim = sims.First (s => s.Name == IOS_SIM_NAME && s.Runtime == IOS_SIM_RUNTIME);

Simulator が見つかったら、起動して起動が終わるまで待つ (BootAppleSimulator (sim.UDID);)

// Boot the simulator
Information("Booting: {0} ({1} - {2})", sim.Name, sim.Runtime, sim.UDID);
if (!sim.State.ToLower().Contains ("booted"))
    BootAppleSimulator (sim.UDID);

// Wait for it to be booted
var booted = false;
for (int i = 0; i < 100; i++) {
    if (ListAppleSimulators().Any (s => s.UDID == sim.UDID && s.State.ToLower().Contains("booted"))) {
        booted = true;
        break;
    }
    System.Threading.Thread.Sleep(1000);
}

ビルドした IPA ファイルを Simulator にインストールする部分 (InstalliOSApplication(sim.UDID, MakeAbsolute(ipaPath).FullPath))

// Install the IPA that was previously built
var ipaPath = new FilePath(IOS_IPA_PATH);
Information ("Installing: {0}", ipaPath);
InstalliOSApplication(sim.UDID, MakeAbsolute(ipaPath).FullPath);

バイスからテスト結果を受け取る TCP リスナーを起動する

// Start our Test Results TCP listener
Information("Started TCP Test Results Listener on port: {0}", TCP_LISTEN_PORT);
var tcpListenerTask = DownloadTcpTextAsync (TCP_LISTEN_PORT, IOS_TEST_RESULTS_PATH);

IPA を起動(単体テストアプリを起動)して、TCP リスナが結果を取得するのをまって、出来上がったファイルを成型する

// Launch the IPA
Information("Launching: {0}", IOS_BUNDLE_ID);
LaunchiOSApplication(sim.UDID, IOS_BUNDLE_ID);

// Wait for the TCP listener to get results
Information("Waiting for tests...");
tcpListenerTask.Wait ();

AddPlatformToTestResults(IOS_TEST_RESULTS_PATH, "iOS");

Simulator をシャットダウンする

// Close up simulators
Information("Closing Simulator");
ShutdownAllAppleSimulators ();

結果、コメントに書いてあることを素直に追いかけるだけで理解できた。

まとめ

iOS の自動ビルド(テスト)の部分読みましたが、他のプラットフォームでも同じようなことをやっているようです。このようなスクリプトを自分のアプリのビルドにも取り込むと作業が楽になるかもしれません。