②Appium＋JUnitで自動テストを書いてみた【iOS編】


あわせて読みたい:
[①Appiumを使ってみた【iOS編】](http://qiita.com/kzm7/items/36629fe2a3c82e3e4933)
[③Appiumでどこまで出来るか試してみた【iOS編】](http://qiita.com/kzm7/items/85c31bda0fbe5f90a5b2)
[④AppiumでWebアプリのテストを実機で実行する【iOS編】](http://qiita.com/kzm7/private/1701a944784df76a9315)

# 1. はじめに

[前回の投稿](http://qiita.com/kzm7/items/36629fe2a3c82e3e4933)では、Appiumの導入〜簡単なテストの実行方法までについて学びました。
今回はより実践的な内容として **JUnitで書かれたJavaのテストコード** を使用した方法について記述していきたいと思います。
　
　　
# 2. 事前準備

**1.  `TestApp`を実機にインストールする**
前回に引き続き、アプリはAppiumのサンプルコードコードに同梱されている`TestApp`を使用します。
Xcodeを使用して実機へアプリのビルド＆インストールを行います。※詳細な手順は[前回の投稿](http://qiita.com/kzm7/items/36629fe2a3c82e3e4933)を参照
　
**2. Mavenをインストールする**

Javaで書かれたテストの実行には`Maven`が必要となりますので、あらかじめインストールしておきます。

```
brew install maven
```
　
　
# 3. 簡単なテストコードの例

JUnitで書かれた[公式のサンプルコード](https://github.com/appium/appium/tree/master/sample-code/examples/java/junit)を参考に、`TestApp`をテストするシンプルなテストコードを書いてみました。


```java:MySimpleTest.java
package com.saucelabs.appium;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileBy;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.openqa.selenium.*;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;

public class MySimpleTest {
  private AppiumDriver driver;

  // 各テストメソッド実行前に呼ばれる処理
  @Before
  public void setUp() throws Exception {
    // Appiumの設定
    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability("platformName", "iOS");
    capabilities.setCapability("app", "io.appium.TestApp");

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

  // 各テストメソッド実行後に呼ばれる処理
  @After
  public void tearDown() throws Exception {
    driver.quit();
  }

  // テストメソッド
  @Test
  public void testCalc() throws Exception {
    // 要素の取得
    WebElement tf1 = driver.findElement(By.xpath("//UIATextField[1]"));
    WebElement tf2 = driver.findElement(By.xpath("//UIATextField[2]"));
    WebElement btn = driver.findElement(By.xpath("//UIAButton[1]"));
    WebElement sumLabel = driver.findElementsByClassName("UIAStaticText").get(0);

    // 要素を操作
    tf1.sendKeys("12");
    tf2.sendKeys("87");
    btn.click();

    // テスト結果の検証
    assertEquals("99", sumLabel.getText());
  }
}
```

## ポイント

`setUp()`メソッドではAppiumの設定を行っています。
実機でテストを行う場合は、少なくても以下の項目を設定する必要があるようです。

- platformName
- app
- udid


```java
// 各テストメソッド実行前に呼ばれる処理
  @Before
  public void setUp() throws Exception {
    // Appiumの設定
    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability("platformName", "iOS");
    capabilities.setCapability("app", "io.appium.TestApp");

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

※指定可能な項目の一覧は[こちら](https://github.com/appium/appium/blob/master/docs/en/caps.md)を参照

これらの値はAppiumを起動するときの引数でも指定が可能です。
実際の運用を想定して、上記の例では`udid`はテストコード内に記述していません。

なお、`Appiumを起動する際の引数`と`DesiredCapabilities`の両方を指定した場合は、`DesiredCapabilities`の値が優先して使用されるようです。



　
## JUnitのテストを実行するには
1.上記のソースコードを`MySimpleTest.java`という名前で下記ディレクトリに保存します。

`/appium/sample-code/examples/java/junit/src/test/java/com/saucelabs/appium`

2.AppiumをUDIDを指定して起動します。

```
appium -U "<実機のUDID>" &
```

3.`junit`フォルダにカレントディレクトリを移動します。

```
cd appium/sample-code/examples/java/junit
```

4.以下のコマンドでテストを実行します。

```
mvn -Dtest=com.saucelabs.appium.MySimpleTest test
```
　
　
# 4. 要素の指定方法

## クラス名で指定

要素をクラス名（種類）で指定する方法です。

```クラス名
driver.findElementByClassName("UIAButton");
driver.findElement(By.className("UIAButton"));
```

クラス名には以下の値が指定可能です。※詳しくは[iOS Developer Library](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/_index.html)を参照

```指定可能なクラス名
UIAActionSheet
UIAActivityIndicator
UIAActivityView
UIAAlert
UIAApplication
UIAButton
UIACollectionView
UIAEditingMenu
UIAElement
UIAElementArray
UIAHost
UIAKey
UIAKeyboard
UIALink
UIALogger
UIANavigationBar
UIAPageIndicator
UIAPicker
UIAPickerWheel
UIAPopover
UIAProgressIndicator
UIAScrollView
UIASearchBar
UIASecureTextField
UIASegmentedControl
UIASlider
UIAStaticText
UIAStatusBar
UIASwitch
UIATabBar
UIATableCell
UIATableGroup
UIATableView
UIATarget
UIATextField
UIATextView
UIAToolbar
UIAWebView
UIAWindow
```

## IDで指定
要素に`accessibilityIdentifier`や`accessibilityLabel`が設定されている場合は、IDとして指定が可能です。

```ID
driver.findElementById("ComputeSumButton"));
driver.findElement(By.id("ComputeSumButton"));
```

## XPathで指定
XPathを使用して指定することもできます。

```XPath
driver.findElement(By.xpath("//UIANavigationBar//UIAButton[3]"));
```

## 複数の要素を取得する場合
複数の要素を取得するには`findElementsBy`を使用します。

```
driver.findElementsByClassName("UIATextField");
driver.findElementsByClassName("UIAStaticText").get(0);
```
　
　
# 5. 要素の操作方法

## 操作系
### タップ
```ボタンをタップ
driver.findElement(By.xpath("//UIAButton[1]")).button.click();
```

### 文字入力、クリア
```TextFieldに文字を入力
driver.findElement(By.xpath("//UIATextField[1]")).sendKeys("12");
```
```TextFieldの値をクリア
driver.findElement(By.xpath("//UIATextField[1]")).clear();
```

### スワイプ
要素をスワイプするには`swipe`コマンドを使用します。
※`duration`の値は500〜6000(ミリ秒)の範囲で指定する必要があります。

```
swipe(int startX, int startY, int endX, int endY, int duration);
````

```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, 500);
```
　
## 取得系

### テキストを取得

```TextFieldの値を取得
String str = driver.findElement(By.xpath("//UIATextField[1]")).getText()
```

```AlertViewに表示されている文字列を取得
Alert alert = driver.switchTo().alert();
String str = alert.getText();
```

### 指定したアトリビュート値を取得

```Sliderの値を取得
WebElement slider = driver.findElement(By.xpath("//UIASlider[1]"));
String str = slider.getAttribute("value");
```

### 位置を取得

```ボタンの位置を取得
Point pos = driver.findElement(By.xpath("//UIAButton[1]")).getLocation();
```

### サイズを取得

```ボタンのサイズを取得
Dimension size = driver.findElement(By.xpath("//UIAButton[1]")).getSize();
```

### 表示されているかどうか

```ボタンが表示されている事を確認
WebElement button = driver.findElement(By.xpath("//UIAButton[1]"));
assertEquals(true, button.isEnabled());
```

### 有効かどうか

```ボタンが有効である事を確認
WebElement button = driver.findElement(By.xpath("//UIAButton[1]"));
assertEquals(true, button.isDisplayed());
```