③Appiumでどこまで出来るか試してみた【iOS編】


あわせて読みたい:
[①Appiumを使ってみた【iOS編】](http://qiita.com/kzm7/items/36629fe2a3c82e3e4933)
[②Appium＋JUnitで自動テストを書いてみた【iOS編】](http://qiita.com/kzm7/items/187ef263b6ac3ba7b3bd)
[④AppiumでWebアプリのテストを実機で実行する【iOS編】](http://qiita.com/kzm7/private/1701a944784df76a9315)


# はじめに
Appiumを実際の現場で導入するにあたり、Appiumがスマホ特有の操作をどこまでサポートしているのか気になるところです。
今回は、Appiumでどのような操作が出来て何が出来ないのか、実際にいろいろ試してみました。

なお前回に引き続き、テストコードは **Java** 、端末は **iPhone5s(iOS7.1.1)** を使用します。

　
# 基本編
## タップ
### 要素を指定してタップ
シンプルにタップするだけであれば`click`コマンドが使用できます。

```java:ボタンをタップ
driver.findElementByClassName("UIAButton").click();
```


### 座標を指定してタップ
画面内で座標を指定してタップしたい場合は`tap`コマンドを使用します。

```java:
tap(int fingers, int x, int y, int duration) 
```

- `fingers`: タップする指の本数
- `x`: タップするX座標
- `y`: タップするY座標
- `duration`: タッチダウンしてから指を離すまでの秒数（ミリ秒）

```java:画面のX=100,Y=150の位置をタップ
WebElement button = driver.findElementByClassName("UIAButton");
driver.tap(1, 100, 150, 1000);
```

　
## ダブルタップ
[Googleフォーラム](https://groups.google.com/forum/#!msg/appium-discuss/5Tc8J-xw9TU/k8T0S1MSFxsJ)のJonathan Lipps氏（Appiumの開発者）の回答を見る限りでは、現時点で`doubleClick`、`doubleTap`のようなコマンドは実装されていないようです。

ただ、AppiumのコアであるUIAutomationには`doubleTap`コマンドが用意されているので、そのコマンドを直接実行することでダブルタップを行うことは可能です。

```java:ボタンをダブルタップ
driver.executeScript("target.frontMostApp().mainWindow().buttons()['Button'].doubleTap();");
```

　
## ロングタップ
`tap`コマンドの`duration`によってタッチダウンしてから指を離すまでの秒数を制御することが出来るので、ロングタップも可能です。

```java:
tap(int fingers, org.openqa.selenium.WebElement element, int duration) 
```
- `fingers`: タップする指の本数
- `element`: 操作対象の要素
- `duration`: タッチダウンしてから指を離すまでの秒数（ミリ秒）

```java:2本の指でボタンを3秒間タップ(ロングタップ)
WebElement button = driver.findElementByClassName("UIAButton");
driver.tap(2, button, 3000);
```

　
## スワイプ
`swipe`コマンドでは、座標を指定してスワイプ操作をシミュレートすることが出来ます。

```java:
swipe(int startx, int starty, int endx, int endy, int duration) 
```
- `startx`: 開始X座標
- `starty`: 開始Y座標
- `endx`: 終了X座標
- `endy`: 終了Y座標
- `duration`: スワイプにかける時間　※500〜6000(ミリ秒)の範囲で指定

```java:TableViewの3セル目をスワイプして削除ボタンを表示させる
WebElement cell = driver.findElement(By.xpath("//UIATableView[1]/UIATableCell[3]"));
Point pos = cell.getLocation();
driver.swipe(pos.getX() + 200 , pos.getY() + 10, pos.getX() + 50, pos.getY() + 10, 1000);
```

　
## フリック
`swipe`コマンドの`duration`（移動距離）を小さくすることで、フリック操作をシミュレート可能です。

```java:画面をフリック
driver.swipe(100, 100, 130, 100, 500);
```
　
## ピンチイン
`zoom`コマンドでは指定した座標を起点としてピンチイン操作をシミュレートします。

```java:
zoom(int x, int y)
```
- `x`: ズーム開始X座標
- `y`: ズーム開始Y座標

```java:座標X=100,Y=160の位置を基準にピンチイン
driver.zoom(100, 160);
```

　
## ピンチアウト
`pinch`コマンドでは指定した座標を起点としてピンチアウト操作をシミュレートします。

```java:
pinch(int x, int y)
```
- `x`: ピンチ開始X座標
- `y`: ピンチ開始Y座標

```java:座標X=100,Y=160を基準にピンチアウト
driver.pinch(100, 160);
```

　
## シェイク
`shake`コマンドを使用することでシェイク操作をシミュレート可能です。

```java:端末をシェイク
driver.shake();
```

　
# 端末操作編
## アプリをインストール
Appium起動時の引数またはテストコード内で、`app`オプションに*.ipaファイルのフルパスを指定することで、テスト実行時にアプリのインストールを行うことが出来ます。

```java:SimpleTest.java
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("platformName", "iOS");
capabilities.setCapability("app", "/Users/taro/Appium/TestApp.ipa");

driver = new AppiumDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
```

　
## アプリをアンインストール
上記の方法によってテスト実行時にインストールされたアプリは、テスト終了後には自動的にアンインストールされます。
もしアンインストールしたくない場合は、Appium起動時に`--no-reset`オプションを指定します。

```
appium --no-reset --app "/Users/taro/Appium/TestApp.ipa" -U <実機のUDID> &
```

　
## アプリを起動
テストシナリオ内でアプリを起動することができます。
このコマンド単体ではあまり使用用途がないように思いますが、後述の`closeApp`コマンドと組み合わせることで、アプリの再起動操作をシミュレートすることが出来ます。

```java:アプリを起動
driver.launchApp();
```

ちなみに「アプリ間連携のテストに使えるかも？」と期待したのですが、どうやら引数にbundleIdを指定できるわけではないので、テスト実行中のアプリ以外は起動することはできなそうです。

　
## アプリを終了
実行中のアプリを終了します。
なお、`launchApp`と同様、bundleIdを指定してアプリを終了することはできません。

```java:アプリを終了
driver.closeApp();
```

　
## アプリをバックグラウンドへ移行
指定した秒数の間、アプリをバックグラウンドへ移行（ホーム画面を表示）します。

```java:
driver.runAppInBackground(int seconds) 
```
- `seconds`: バックグラウンドを維持する時間（秒）

```java:アプリをバックグランドへ移行して3秒後に元に戻す
driver.runAppInBackground(3);
```

　
## 画面をロック
端末の画面をロックし、指定した秒数の後にロックを解除します。
なお、端末にパスワードが設定されている場合はロックの解除は出来ません。

```java:
driver.lockScreen(int seconds) 
```

- `seconds`: ロック状態を維持する時間（秒）

```java:端末を3秒間ロック
driver.lockScreen(3)
```

　
## 端末を回転
指定した向き（Landscape/Portrait）に端末を回転します。

```java:
driver.rotate(org.openqa.selenium.ScreenOrientation orientation) 
```

- `orientation`: 端末の向き

```java:端末を横向き(Landscape)に回転
driver.rotate(ScreenOrientation.LANDSCAPE);
```

```java:端末を縦向き(Portrait)に回転
driver.rotate(ScreenOrientation.PORTRAIT);
```
　
--
> 参考
http://appium.github.io/java-client/
https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html