tafuji's blog

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

Xamarin.iOS 単体テストを Visual Studio Team Services 上で動作させる方法

Qiita より転載

はじめに

この資料は、Xamarin.iOS の Unit Test を VSTS 上で実行する手順について記載します。

  • Xamarin Blog の記事をベースに検証を行いました
    • Blog 記事をそのまま検証するだけでなく、工夫を加えている箇所があります
  • Simulator 上で単体テストを実施しています

サンプルコード

検証に利用したソースコードを、GitHub に公開しています。

Solution

今回の検証では、iOS のクラスライブラリを単体テストする場合を考えます。

プロジェクト名 説明
XamarinUnitTestsVsts.iOS.Service 単体テスト対象のプロジェクト。サンプルでは、Calculator クラスの単体テストを行っています
XamarinUnitTestsVsts.iOS.Service.Test 単体テストプロジェクト。サンプルでは、意図的にテストを失敗させています

手順

具体的な手順を記載します。

Touch.Server.exe をダウンロードする

Touch.Server.exe は、Xamarin.iOS 単体テストをサポートするツールで、このツールを利用して単体テストを実行すると、テスト結果のレポートファイルを容易に取り出すことができます。

このツールをダウンロードし、テストプロジェクトのディレクトリに配置します。

Touch.Server.exe

1. Unit Test プロジェクトの設定

ビルド時に以下の環境変数を設定するように、ビルドの設定を修正します。具体的には、プロジェクトの iOS Build の設定に以下の設定を追加します。

環境変数
NUNIT_ENABLE_XML_OUTPUT true
NUNIT_SKIP_LOG_HEADER true

今回は、VSTS 上で Simulator を動作させるため、ビルドの構成は、'Debug','iPhoneSimulator' で、設定を追加しています。

iOSBuildSettings

2. Touch.Server を起動する Shell スクリプトを作成する

Touch.Server.exe を起動するための Shell スクリプトを作成します。

#!/bin/sh
echo 'Output path = ' $1
TEST_RESULT=$1/test_results.xml
echo 'Delete test result'
rm -rf $TEST_RESULT
mono --debug $1/Touch.Server.exe \
--launchsim  ./bin/iPhoneSimulator/Debug/XamarinUnitTestsVsts.iOS.Service.Test.app \
-autoexit \
-skipheader \
-logfile=$TEST_RESULT \
--verbose \
--device=":v2:runtime=com.apple.CoreSimulator.SimRuntime.iOS-11-3,devicetype=com.apple.CoreSimulator.SimDeviceType.iPhone-8"

ビルドサーバーの環境によって、--device のオプションの値を適宜修正する必要があります。 検証を行った環境では、iOS 11.3 の iPhone 8 の Simulator を利用しています。

3. Visual Studio Team Services のビルド設定

ここでは、VSTS 上のビルドタスクに関する説明を記載します。

0. ビルドの流れ

VSTS で Xamarin.iOS のビルドタスクを作成し、単体テストを実行するために必要なビルドタスクを追加します。今回の検証では、以下の図の (1) ~ (6) のタスクを追加しています。

BuildTasks

# VSTS タスク 概要
1 MSBuild 単体テストプロジェクトをビルドするタスクです
2 Shell Script Touch.Server.exe を起動し、単体テストを実行するための Shell スクリプトを実行するタスクです
3 Publish Test Results テストレポートのファイルを VSTS に登録するタスクです
4 Set variable with value from XML テストレポートファイルから、エラー件数を取得し、VSTS の変数にその値を格納するタスクです
5 Set variable with value from XML テストレポートファイルから、失敗の件数を取得し、VSTS の変数にその値を格納するタスクです
6 Command Line テストレポートに、エラー、失敗があった場合にビルドを失敗させるためのタスクです

#4 ~ #6 のタスクが、参考にした Xamarin Blog に工夫を加えた点です。実は、VSTS の "Publish Test Reslts" タスクは、失敗したテストケース結果が含まれるレポートを登録しても、ビルドタスクを成功扱いにしてしまいます。参考にした記事ではこの点については、GitHub で議論されている最中だと述べるにとどめています。

そこで、本記事では、以下に記載する方法で、テストレポートにテストケースの失敗やエラーがあれば、ビルドが失敗扱いとなるようにビルドタスクを組み込んでいます。

  • 単体テスト完了後、テストレポートファイルを解析する
  • テストレポートにエラーが含まれていたら、ビルドタスクを失敗させる
テストレポートファイルの解析

テストレポートを解析する方法には、いろいろな方法が考えられます。テストレポートのファイルは、XML ファイルであり、test-results 要素の属性にテストの成功、失敗などの件数が格納されていますので、この値を VSTS のビルドタスクで取得する方法を考えます。

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!--This file represents the results of running a test suite-->
<test-results
    name=""
    total="5"
    errors="0"
    failures="2"
    not-run="1"
    inconclusive="0"
    ignored="1"
    skipped="0"
    invalid="0"
    date="2018-04-19" time="08:28:42">
    ... 省略
</test-results>

今回の検証では、VSTS拡張機能Variables Tasks Pack)を利用して、test-results 要素の errors, failures 属性の値を取得し、VSTS の変数に格納し、後述の Command Line タスクでテストの成功・失敗の判定に利用しています。

Command Line タスク

VSTS で提供される Command Line タスクは、標準エラーに出力を行った場合には、タスクをエラー扱いとすることができます。

Command Line タスクの Script で、echo Failed 1>&2 のように標準ラー出力を行い、'Advanced' の設定で 'Faile on Standard Error' にチェックを入れることで標準エラーに何らかの出力を行うとタスクをエラーとすることができます。

FailureCommand1

テストレポートにエラーが含まれるという条件判定には、VSTS の Control Option の Custom condition を利用します。

FailureCommand2

標準エラー出力を行って、ビルドタスクを失敗される方法は、以下のサイトを参考にしました。

1. ビルドタスク

MSBuild タスクを利用して、ユニットテスト対象のプロジェクトをビルドします。ビルド対象のプロジェクトに単体テストプロジェクトを指定します。

  • Project
    • 単体テストプロジェクトのプロジェクトファイルを指定します

Build-iOS

2. Shell Scrpt を実行する

Shell Script タスクを利用して、Touch.Server.exe を起動する Shell スクリプトを実行します。

  • Script Path
    • Touch.Server.exe を起動する Shell Script ファイルを指定します
  • Arguments

ScriptTask

3. テストレポートの登録

Publish Test Results タスクを利用して、テストレポートを VSTS に登録します。

  • Test results files
    • テストレポートのファイルを指定します
  • Search folder
    • テストレポートが出力されるフォルダを指定します
      • レポートの出力フォルダは、Touch.Server.exe を起動する Shell スクリプトを参照してください

PublishTestResults

4. エラー件数の値の取得と VSTS 変数への格納

Set variable with value from XML ビルドタスク(拡張機能)を利用して、レポートファイルを解析し、エラーの件数を VSTS の変数に格納します。

  • Variable Name
    • 取得した値を格納する変数名を指定します
      • 今回の検証では、NUnitTestResult.Erros という名前の変数に値を格納しています
  • XPath expression
    • 取得したい XML の要素(属性)を XPath 形式で記述します。ここでは、 errors 属性の値を取得しています
  • XML file path

GetErrorCount

5. 失敗した件数の値の取得と VSTS 変数への格納

Set variable with value from XML ビルドタスク(拡張機能)を利用して、レポートファイルを解析し、テスト失敗の件数を VSTS の変数に格納します。

  • Variable Name
    • 取得した値を格納する変数名を指定します
      • 今回の検証では、NUnitTestResult.Failures という名前の変数に値を格納しています
  • XPath expression
    • 取得したい XML の要素(属性)を XPath 形式で記述します。ここでは、 failures 属性の値を取得しています
  • XML file path

GetFailuresCount

6. テストレポートにエラーが含まれていた場合に、ビルドを失敗にする

Command Line タスクを利用して、単体テストのレポートにエラー・失敗が含まれていた場合は、ビルドタスクを失敗させます。

  • Script
    • 標準エラーにメッセージを出力します
  • Advanced の設定で Fail on Standard Error にチェックを入れます

FailureCommand1

  • Control Options
    • Run this taskCustom conditions を選択します
    • Custom Condition で以下の条件を入力します
      • 以下のいずれかの場合にタスクが失敗します
        • すでにビルドが失敗している場合
        • NUnitTestResult.Erros 変数の値が 0 でない場合
        • NUnitTestResult.Failures 変数の値が 0 でない場合
or(failed(), ne(variables['NUnitTestResult.Erros'], '0'), ne(variables['NUnitTestResult.Failures'], '0'))

FailureCommand2

VSTS の Custom condition に関する詳細は、以下を参照してください。

実行

VSTS 上でビルドタスクを実行すると、単体テストが実行され、テスト結果のレポートが登録されることがわかります。

BuildResults

おわりに

Touch.Server.exe を利用することで、iOS単体テストプロジェクトを自動ビルドプロセスの中に組み込むことができるようになることが確認できました。

Android の場合はどうなるのかと思う方がいるかもしれませんが、Android 向けに Touch Server For Android というツールが GitHub で公開されています。このツールを利用した VSTS 上での単体テストの実行方法については、後日検証してみたいと考えています。

参考