Xamarin Test Recoderを触ってみた


この記事は、[Xamarin(その2) Advent Calendar 2016](http://qiita.com/advent-calendar/2016/xamarin2) の18日目の記事です。
Xamarin Test Recoderを使ってできることを一通り試してみたいと思います。


#  Why Xamarin Test Recorder
モバイル向けのUIテストフレームワークとして有名なところで[Appium](http://appium.io/)があります。
AppiumはC#、Java,JSなど多様な言語でUIオートメーションテストを行うことができますが、
Appium.App(exe)というスタンドアロンアプリで、UIオートメーションテストを行うために必要なテストコードを自動で生成してくれる補助ツールもあります。
これがなかなかイケていて、アプリをぽちぽち操作するだけでテストコードを自動生成してくれます。


一方、Xamarin社が提供するテストフレームワークには、Rubyで記載する[Calabash](http://calaba.sh/)やC#で記述する[Xamarin.UITest](https://developer.xamarin.com/guides/testcloud/uitest/intro-to-uitest/)があります。
いずれもUIオートメーションのためのフレームワークとしてやれることはAppiumとあまり変わりませんが、テストコードを記述するには、[REPLなどを使って調査しながら書いていく](http://ytabuchi.hatenablog.com/entry/xamarinuitest)形であり、それ自体にはAppium.Appのようなテストコード自動生成機能はありません。


そこでXamarin Test Recorderですよ！

# Xamarin Test Recoderとは

## 概要
[Xamarin.UITest](https://developer.xamarin.com/guides/testcloud/uitest/intro-to-uitest/)でのUIオートメーションテストを行うために必要なテストコードを自動で生成してくれる補助ツールです。
https://www.xamarin.com/test-cloud/recorder


## できること

* Simulatorを利用したUIテストのレコーディング
* 実機を利用したUIテストのレコーディング
* Xamarin.UITestコードのエクスポート
* Xamarin Test Cloudへのアップロード


## 提供形態
| OS | 提供形態 | ライセンス |
|:--|:--|:--|
| Windows | VS2013 or VS2015 の拡張機能 | VS Enterprise subscription |
| Mac | Xamarin Test Recorder(スタンドアロンアプリ) |  |

## 必要条件
| 項目 | iOS | Android | 備考 |
|:--|:--|:--|:--|
| Minumum Ver | 8.0〜 | 4.3 (API level 19) | 
| Package | App(Simulator),IPA(実機) | APK | | 


## Xamarin Test Recoderの画面構成
アプリを開くと、4ペインで構成されています。
![TestRecorder-parts-xs.png](https://qiita-image-store.s3.amazonaws.com/0/75755/8b3ea513-5b6f-bc0e-8848-4473032911aa.png "TestRecorder-parts-xs.png")

①でレコード開始と停止、再生とファイル出力ができます。
②レコード対象のデバイスとアプリを指定します。
③レコーダーによって記録された操作が表示されます。
④レコーダーによって記録された操作がC#コードに変換されたものが表示されます。


それでは、実際に試してみましょう。
# Xamarin Test Recorder触ってみよう

## Xamarin Test Recorderのインストール
Test RecorderはVisual StudioやXamarin Studioに標準でインストールされないので、以下のHPよりダウンロードしましょう。
https://www.xamarin.com/test-cloud/recorder

Mac版はこんなアイコンでした。

![TestRecorderIcon.png](https://qiita-image-store.s3.amazonaws.com/0/75755/43c5b5c4-8ef9-1087-0474-b84f5dc2532d.png "TestRecorderIcon.png")

インストール完了したら、早速開いてみましょう。

![TestRecorderHome.png](https://qiita-image-store.s3.amazonaws.com/0/75755/25d9d731-bfae-7703-da34-6acacdc86525.png "TestRecorderHome.png")

USBで接続されたデバイスとシミュレータが選択できます。
![TestRecorder_selectDevice.png](https://qiita-image-store.s3.amazonaws.com/0/75755/c9355ae7-c003-f292-9070-a08157442e42.png 
"TestRecorder_selectDevice.png")
ここでは、シミュレータを選択します。

次に、Appで対象のアプリを選択します。
ここでは、Xamarin Dev DaysのHandsOnで使用した[DevDaysSpeakers](https://github.com/xamarin/dev-days-labs/tree/master/HandsOnLab)を使用してみます。
iPhoneSimulatorをターゲットにして、ビルドすると、
{RootDir}/DevDays/HandsOnLab/Finish/DevDaysSpeakers/DevDaysSpeakers.iOS/bin/iPhoneSimulator/Debug
の中に、 **DevDaysSpeakersiOS.app** が生成されるので、これを選択します。

そうすると自動的にシミュレータを立ち上げ、アプリをシミュレータにインストールし、起動しました。途中でInternetの接続許可が出るので、OKしています。
では、早速メイン機能である、レコーディングをやってみましょう。


## Record
まずは以下をご覧ください。
![TestRecorder_Record.gif](https://qiita-image-store.s3.amazonaws.com/0/75755/2c821884-2769-4930-937e-6d9ce9c65581.gif "TestRecorder_Record.gif")

Recordボタン押下後、アプリで「Sync Speakers」を押しています。
自動的にコードと操作が記録されていきます。

```csharp
app.Tap(x => x.Text("Sync Speakers"));
```

リスト表示後、リストの先頭行をタップしています。

```csharp
app.Tap(x => x.Class("UITableViewCellContentView"));
```

その後"Go to Website"を押し、アプリからSafariを開き、
SafariからDevDaysSpeakerに戻る操作を行っていますが、そこはレコードされませんでした。
アプリのスクロールはレコードされています。

```csharp
app.ScrollDown();
app.ScrollUp();
```
ここまででレコードを終わります。

なお、右側ペインのC#コードは直接編集不可です。
左側のペインでは、操作の取り消し、スクリーンショットの追加は可能でした。簡単な変更は可能でしたが、新規操作の追加などはできず、基本はアプリ画面の操作で行うことしかできないと思っていいかと思います。（Assertionの追加などはできない。）

続けてReplayを行なっています。

## Replay

![TestRecorder_Replay.gif](https://qiita-image-store.s3.amazonaws.com/0/75755/e3fd31f1-56e7-4c78-da11-ef01f64ac9ac.gif "TestRecorder_Replay.gif")

リプレイでは、"Go to Website"までは順調に進んでいますが、
やはりSafariからDevDaysSpeakerに戻る操作がないため、それ以降のテストが実施されていません。

Calabashでは、テスト対象アプリ以外の操作はできないため、REPLを使っても、同じように操作できません。

## Export　UITest
ExportからUITest用のコードを出力できます。
出力したファイルを開くと、以下が生成されていました。

```csharp
using System;
using NUnit.Framework;
using Xamarin.UITest;
using Xamarin.UITest.iOS;

[TestFixture]
public class RecorderTest {
	iOSApp app;

	[SetUp]
	public void SetUp()
	{
		app = ConfigureApp.iOS.AppBundle("/Users/masamba/Documents/Development/Xamarin/DevDays/HandsOnLab/Finish/DevDaysSpeakers/DevDaysSpeakers.iOS/bin/iPhoneSimulator/Debug/xtr-DevDaysSpeakersiOS.app").StartApp();
	}


	[Test]
	public void NewTest() {
		app.Tap(x => x.Text("Sync Speakers"));
		app.Tap(x => x.Class("UITableViewCellContentView"));
		app.Tap(x => x.Text("Speak"));
		app.Tap(x => x.Text("Go to Website"));
		app.Tap(x => x.Text("Speakers"));
		app.ScrollDown();
		app.ScrollUp();
	}

}

```

興味深いのは、以下です。

```csharp
app = ConfigureApp.iOS.AppBundle("/Users/masamba/Documents/Development/Xamarin/DevDays/HandsOnLab/Finish/DevDaysSpeakers/DevDaysSpeakers.iOS/bin/iPhoneSimulator/Debug/xtr-DevDaysSpeakersiOS.app").StartApp();
```

アプリの開始要求をしていますが、 **xtr-DevDaysSpeakersiOS.app**となっています。
ディレクトリを見てみると、確かに、 *xtr-* がついたAppができています。
公式を見てみると、こんなことが書いていました。


> When recording tests for an iOS application, the Test Recorder will automatically inject a special library called the Test Recorder Plugin into the app bundle (Android apps do not need this library). The Test Recorder Plugin is extra code that allows the Test Recorder to observe the application that is being tested and generated the C# code for the various events.

> ⚠️
> The presence of the Test Recorder Plugin in an IPA is grounds for the Apple App Store to reject the app bundle; do not submit any IPAs that were used by the Test Recorder to the App Store for distribution.
```

Test Recorder用のPluginがバンドルされたアプリが自動生成され、App StoreにあげたらRejectされるから気をつけろということまで書いています。
ちなみに、Xamarin.UITestや、Calabashを使う場合、Nugetから、 **Xamarin.TestCloud.Agent**パッケージをインストールする必要がありますので、このパッケージがインストールされたと思います。

なお、すでにXamarin.TestCloud.Agentをインストールしてパッケージしている場合は、Xamarin Test Recorderのプレファレンスで
 *Automatically inject Calabash into iOS apps* のチェックをOFFにすると良さそうです。
![TestRecorder_preference.png](https://qiita-image-store.s3.amazonaws.com/0/75755/e07aaa1e-af1a-5a25-2048-ebd4d036947e.png "TestRecorder_preference.png")


## Send to Test Cloud
右上のExportボタンより、 *Send to Test Cloud* ボタンを押してみます。
![スクリーンショット 2016-12-18 2.42.30.png](https://qiita-image-store.s3.amazonaws.com/0/75755/c88f7e10-dab4-5b2c-890e-adf03773220a.png "スクリーンショット 2016-12-18 2.42.30.png")

シミュレータ用の.appではなく、IPAをよこせと怒られたので、
![スクリーンショット 2016-12-18 3.11.45.png](https://qiita-image-store.s3.amazonaws.com/0/75755/af6ef0c0-3e92-710a-3d6f-b8dd00836010.png "スクリーンショット 2016-12-18 3.11.45.png")

ついでにWebsiteを開くところを省いて以下とし、再度実行しました。

```
[Test]
public void NewTest() {
	app.Tap(x => x.Text("Sync Speakers"));
	app.Tap(x => x.Marked("Cliff Sentell"));
	app.Tap(x => x.Text("Speakers"));
	app.ScrollDown();
	app.ScrollUp();
}
```


そうするとブラウザが起動し、Xamarin Test Cloudのサイトに誘導されました。

![スクリーンショット 2016-12-18 16.59.21.png](https://qiita-image-store.s3.amazonaws.com/0/75755/f6fecff2-f0b1-cb96-5025-55cfc30fdcf1.png "スクリーンショット 2016-12-18 16.59.21.png")

デバイスを選択し進めると、すでにApp BundleのValidatingが始まっていました。
![スクリーンショット 2016-12-18 17.00.40.png](https://qiita-image-store.s3.amazonaws.com/0/75755/c649dc06-a82b-a376-2d0c-0b2a9fe258c3.png "スクリーンショット 2016-12-18 17.00.40.png")
通常は、Xamarin Test CloudのCLIか、Xamarin Studioからパッケージをアップロードする必要がありますが、Xamarin Test Recorderからアップロードができるということのようですね。

そうこうする間に、Runningになって、テストが実行されていました。
結果、一応成功ということでした。

![スクリーンショット 2016-12-18 17.15.02.png](https://qiita-image-store.s3.amazonaws.com/0/75755/5b044b7d-444a-8e35-56f8-050e6c51ef9d.png "スクリーンショット 2016-12-18 17.15.02.png")

```
17-12-2016 20:00:07.946 +01:00 - 3 - iOS test running Xamarin.UITest version: 2.0.3
17-12-2016 20:00:08.020 +01:00 - 77 - Using TEST_CLOUD configuration override.
17-12-2016 20:00:08.850 +01:00 - 907 - Running in Test Cloud, on device with iOS8 or higher.
17-12-2016 20:02:07.781 +01:00 - 119834 - Tapping element matching Text("Sync Speakers") at coordinates [ 187.75, 86 ].
17-12-2016 20:02:12.447 +01:00 - 124500 - Tapping element matching Marked("Cliff Sentell") at coordinates [ 117.75, 167.25 ].
17-12-2016 20:02:14.361 +01:00 - 126413 - Tapping element matching Text("Speakers") at coordinates [ 62.5, 42.25 ].
17-12-2016 20:02:14.723 +01:00 - 126775 - Scrolling down
17-12-2016 20:02:16.037 +01:00 - 128089 - Scrolling up
17-12-2016 20:02:16.037 +01:00 - 128089 - Scrolling up with query.
Took final screenshot. { Title: "After test", FileName: "/Volumes/Data/xamarin/workspaces/37800a19-9d4b-40a2-9b8d-d920ff0c49de/workspace/screenshot-final-f5e4658103a1471bb1a1d8ce2f643e86.png", ElapsedMilliseconds: 385 }
Completed run. 19 events collected.
```

実際は、非同期処理や画面遷移などの結果、特定の文字が表示されるまでWaitするだとか、操作の結果をAssertionする必要があるので、
直接Test Cloudにあげることはあまりないでしょう。

Xamarin.UITestをTest Recorderを使わずに記述するには、REPLを使って調べながら実施していくことになるので、それよりは楽かもしれません。

なお、Test Recorderで操作した結果は、C#コードとして出力できるほか、Test Recorderでリプレイするために設定ファイル(.xtrs)として出力することもできます。

![スクリーンショット 2016-12-18 17.35.37.png](https://qiita-image-store.s3.amazonaws.com/0/75755/6f410071-3807-fa16-58a8-4e3d8af75705.png "スクリーンショット 2016-12-18 17.35.37.png")

.xtrsファイルを開いて、直接編集できたら楽だな！と思い、開いてみましたが、json形式で、整形後20000行超あり、面倒くさそうでしたのでやめました。定義すべき情報が多いので、あまり効率的ではなくやるべきではなさそうです。

## まとめ

* Xamarin Test Recorderを使うと、Xamarin UITest用のUIオートメーションテストコードの自動生成が可能
* Assertionの追加などはTest Recorderではできないため、Test RecorderとXamarin Test Cloudだけで簡単UITestとするのはまだ早い。
* 操作をRecordしたC#コードをExportして、Xamarin UITestで使うのが主流と思われる。
* Calabashには対応していないため、Xamarin.UITestにターゲットされてしまう。Amazon Device Farmなどのクラウドテストサービスでは、Xamarin.UITestをサポートしていない。
* Xamarin.UITestをREPLを使って記述している場合は、非常に楽になるかも！

以上です。
興味が出た方は試してみてはいかがでしょうか。