Selenium Grid、Appium、Windows Application DriverでWindowsアプリケーションのテスト環境を構築する

# はじめに

~~手動テスト絶対許さない~~テスト自動化促進おじさんです:innocent:
(今回の調査で手動テストも良いなーっておもいましたまる)

担当「@umasakiに自動テストの調査してもらうかのぉ」
私「Webの方は進めてひと段落してまっせ！」
担当「Windowsアプリケーションもあるで」
私「それはまだできまへん:rolling_eyes:」
上長「めっちゃ期待してるでワレェェェ！」
私「こうなったらやってやるって！」

ということでヒアリングすると、Windowsアプリケーションでも結構な工数がシステムテストに取られてるっぽい。
ここを打開するべく調査をしました。

**2018/09/13
必要ない設定や問題を追記しました。**


# 利用したソフトウェア

検証するときに利用したソフトウェアです。
[SikuliX](http://sikulix.com/)というソフトにめちゃめちゃ惹かれましたが、Selenium統一のため以下のソフトになりました。

|ソフト|バージョン|備考|
|:--|:--|:--|
|java|1.8.0_161||
|[selenium-server-standalone.jar](https://www.seleniumhq.org/)|3.11.0||
|[Node.js](https://nodejs.org/ja/)|8.11.1||
|[windows-build-tools](https://github.com/felixrieseberg/windows-build-tools)|2.3.0|npmからインストール|
|[Appium](http://appium.io/)|1.8.0|npmからインストール|
|wd|1.6.2|npmからインストール|
|[WinAppDriver](https://github.com/Microsoft/WinAppDriver/releases)|1.0||
|[Windows 10 SDK](https://developer.microsoft.com/ja-jp/windows/downloads/windows-10-sdk)||inspect.exeを利用|


# インストール

Windows10 32bit に環境を構築しました。

### Windows10 を開発者モードに設定

設定 ⇒ 更新とセキュリティ ⇒ 開発者向け ⇒ 開発者モード で設定しました。
![TESTSS.jpg](https://qiita-image-store.s3.amazonaws.com/0/187061/f0f8796c-74de-0f4f-b6c4-84e2c4dd0585.jpeg)

### Node.js のインストール

node-v8.11.1-x86.msi をインストールしました。

### npmでAppiumなどをインストール

サイトにある『簡単なインストールで今すぐAppiumを体験しよう』の内容で進めたところ、Appiumのインストールで速攻つまづきました。

ここから先はコマンドプロンプトを管理者権限で起動して作業しました。(したと思います)

#### 接続の設定

上4つの設定はプロキシ使ってなかったら必要ないと思います。
一番したのコマンドも意味調べてないです。はい。

```console
set HTTP_PROXY=http://proxyserver:8080
set HTTPS_PROXY=http://proxyserver:8080

npm config -g set proxy http://proxyserver:8080
npm config -g set https-proxy http://proxyserver:8080
npm config -g set registry http://registry.npmjs.org/
```

#### Pythonとか入れろとかのエラーを回避

Python入れろーとか色々エラーがでるのですが、このコマンドで解決しました。
便利:hugging:

```console
npm install --global --production windows-build-tools
```

#### Appiumをインストール

ふぃー(・д・｀*)

```console
npm install -g appium
```


**2018/09/13 追記
Windows Application Driverだけの場合、Appium Clientは必要ありません。**

<details><summary>Appium Clientをインストール</summary><div>

#### Appium Clientをインストール

ここだけはコマンドプロンプトを普通に起動して作業しています。

```console
npm install wd
```

</div></details>


### Windows Application Driverをインストール

**2018/09/13 追記
以下のコマンドでもインストールできました。
内部的にはWindowsApplicationDriver.msiを実行しているようなので、コマンドプロンプトを管理者権限で実行しなくてはいけないかもしれません。**

```console
npm install appium-windows-driver
```

WindowsApplicationDriver.msi をインストールしました。


# 環境構築

#### Selenium Grid Hubを立ち上げる

以下のコマンドを実行します。

```console
java -jar selenium-server-standalone-3.11.0.jar -role hub
```

#### AppiumをNodeとして立ち上げる

jsonのconfiguration設定は意味調べてないです。
いらなそうな設定もあるがコピーペ。

```json:NodeConfigAppium.json
{
  "capabilities":
      [
        {
          "platformName":"Windows",
          "deviceName":"WindowsPC",
          "maxInstances": 1
        }
      ],
  "configuration":
  {
    "cleanUpCycle":2000,
    "timeout":30000,
    "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
    "url":"(Node側)http://XXX.XXX.XXX.XXX:4723/wd/hub",
    "host": "(Node側)XXX.XXX.XXX.XXX",
    "port": (Node側)4723,
    "maxSession": 1,
    "register": true,
    "registerCycle": 5000,
    "hubPort": (Hub側)4444,
    "hubHost": "(Hub側)XXX.XXX.XXX.XXX"
  }
}
```

設定ファイルができたら以下のコマンドを実行します。

```console
appium --nodeconfig NodeConfigAppium.json
```


# 実際に動かしてみる

サンプルをちょこっと変えて実行しました。
これで動かすと日本語OSであればコンソールに『表示は 8 です』と表示されます。

Mavenの設定は過去記事の環境に追加したので以下のようになっています。
Appiumだけ考えると結構いらないものが入ってますな。
![aWS050269.JPG](https://qiita-image-store.s3.amazonaws.com/0/187061/b504a725-0b51-9431-47c2-18905fd66709.jpeg)

```java:CalculatorTest.java
package test;

import java.net.URL;
import java.util.concurrent.TimeUnit;

import org.junit.AfterClass;Windows Application Driver
import org.junit.BeforeClass;
import org.junit.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;

import io.appium.java_client.windows.WindowsDriver;

public class CalculatorTest {

    private static WindowsDriver<WebElement> CalculatorSession = null;
    private static WebElement CalculatorResult = null;

    @BeforeClass
    public static void setup() {
        try {
            DesiredCapabilities capabilities = new DesiredCapabilities();
            capabilities.setCapability("app", "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App");
            capabilities.setCapability("deviceName", "WindowsPC");
            capabilities.setCapability(CapabilityType.PLATFORM_NAME, "Windows");

            CalculatorSession = new WindowsDriver<WebElement>(new URL("http://XXX.XXX.XXX.XXX:4723/wd/hub"), capabilities);
            CalculatorSession.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

            CalculatorResult = CalculatorSession.findElementByAccessibilityId("CalculatorResults");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }

    @AfterClass
    public static void TearDown() {
        CalculatorResult = null;
        if (CalculatorSession != null) {
            CalculatorSession.quit();
        }
        CalculatorSession = null;
    }

    @Test
    public void Addition() {
        CalculatorSession.findElementByAccessibilityId("num1Button").click();
        CalculatorSession.findElementByAccessibilityId("plusButton").click();
        CalculatorSession.findElementByAccessibilityId("num7Button").click();
        CalculatorSession.findElementByAccessibilityId("equalButton").click();
        System.out.println(CalculatorResult.getText());
    }
}
```

# 要素を調べる

2018/07/20 追記
下記より[WinAppDriver UI Recorder](https://qiita.com/umasaki/items/f9064eaab8a509c8d269)で調べた方が早いです。

Windows 10 SDKをインストールすると入る inspect.exe を利用します。
私の環境では C:\Program Files\Windows Kits\10\bin\10.0.16299.0\x86\inspect.exe にいました。

我流ですが・・・inspect.exe を起動して Always on Top のチェックを外します。

![aWS050268 - コピー (2).JPG](https://qiita-image-store.s3.amazonaws.com/0/187061/b17dd1e4-9314-4b60-193f-3af51ad7aecc.jpeg)

調査したいアプリケーション(ここでは電卓)を起動し、カーソルをしばらく合わせるかクリックし、AutomationIdを調べソースに反映していきました。


![aWS050268 - コピー.JPG](https://qiita-image-store.s3.amazonaws.com/0/187061/a137727f-1c5b-668f-f266-66fab7998f40.jpeg)


# 困っていること

**2018/09/13 追記
以下の困りごとは appium 1.9.0 で直ってました:hugging:**

<details><summary>存在しない要素を指定すると指定時間でエラーにならず10分以上待つ</summary><div>

#### 存在しない要素を指定すると指定時間でエラーにならず10分以上待つ

ログをの出力方法を変えて調査してみる。

##### appium 起動時の引数

```console
appium --nodeconfig NodeConfigAppium.json --log "C:/Users/XXXXX/Desktop/node/log.txt" --log-level "info" --log-timestamp --local-timezone
```

##### 出力ログ

```console
〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓
●電卓の結果要素を取得してるはず
2018-05-02 14:33:07:512 - info: [HTTP] --> POST /wd/hub/session/4b9d3b16-2dd0-4e79-82f8-ce5694e514c6/element
2018-05-02 14:33:07:512 - info: [HTTP] {"using":"accessibility id","value":"CalculatorResults"}
2018-05-02 14:33:07:512 - info: [W3C] Driver proxy active, passing request on via HTTP proxy
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] ==========================================
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] POST /wd/hub/session/6DF966FD-DB90-4ECC-B490-552323C225FF/element HTTP/1.1
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] Accept: */*
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] Connection: keep-alive
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] Content-Length: 56
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] Content-Type: application/json
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] Host: 127.0.0.1:4724
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] User-Agent: appium
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:07:590 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:07:622 - info: [WinAppDriver] [STDOUT] HTTP/1.1 200 OK
2018-05-02 14:33:07:622 - info: [WinAppDriver] [STDOUT] Content-Length: 100
2018-05-02 14:33:07:622 - info: [WinAppDriver] [STDOUT] Content-Type: application/json
2018-05-02 14:33:07:622 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:07:622 - info: [WinAppDriver] [STDOUT] {"sessionId":"6DF966FD-DB90-4ECC-B490-552323C225FF","status":0,"value":{"ELEMENT":"42.3540260.3.5"}}
2018-05-02 14:33:07:622 - info: [JSONWP Proxy] Replacing sessionId 6DF966FD-DB90-4ECC-B490-552323C225FF with 4b9d3b16-2dd0-4e79-82f8-ce5694e514c6
2018-05-02 14:33:07:622 - info: [HTTP] <-- POST /wd/hub/session/4b9d3b16-2dd0-4e79-82f8-ce5694e514c6/element 200 117 ms - 100
〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓
●存在しない要素を取得してるはず
2018-05-02 14:33:32:841 - info: [HTTP] --> POST /wd/hub/session/4b9d3b16-2dd0-4e79-82f8-ce5694e514c6/element
2018-05-02 14:33:32:841 - info: [HTTP] {"using":"accessibility id","value":"num1Buttonabc"}
2018-05-02 14:33:32:841 - info: [W3C] Driver proxy active, passing request on via HTTP proxy
2018-05-02 14:33:33:044 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:33:044 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:33:044 - info: [WinAppDriver] [STDOUT] ==========================================
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] POST /wd/hub/session/6DF966FD-DB90-4ECC-B490-552323C225FF/element HTTP/1.1
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] Accept: */*
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] Connection: keep-alive
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] Content-Length: 52
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] Content-Type: application/json
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] Host: 127.0.0.1:4724
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] User-Agent: appium
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:33:059 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:45:825 - info: [WinAppDriver] [STDOUT] HTTP/1.1 404 Not Found
2018-05-02 14:33:45:841 - info: [WinAppDriver] [STDOUT] Content-Length: 139
2018-05-02 14:33:45:841 - info: [WinAppDriver] [STDOUT] Content-Type: application/json
2018-05-02 14:33:45:841 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:33:45:841 - info: [WinAppDriver] [STDOUT] {"status":7,"value":{"error":"no such element","message":"An element could not be located on the page using the given search parameters."}}
↑↑↑POST返すまでに10分かかってる↓↓↓
2018-05-02 14:43:32:858 - info: [HTTP] <-- POST /wd/hub/session/4b9d3b16-2dd0-4e79-82f8-ce5694e514c6/element - - ms - -
〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓
2018-05-02 14:47:20:139 - info: [HTTP] --> DELETE /wd/hub/session/4b9d3b16-2dd0-4e79-82f8-ce5694e514c6
2018-05-02 14:47:20:139 - info: [HTTP] {}
2018-05-02 14:47:20:139 - info: [Appium] Removing session 4b9d3b16-2dd0-4e79-82f8-ce5694e514c6 from our master session list
2018-05-02 14:47:20:171 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:47:20:171 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:47:20:171 - info: [WinAppDriver] [STDOUT] ==========================================
2018-05-02 14:47:20:171 - info: [WinAppDriver] [STDOUT] DELETE /wd/hub/session/6DF966FD-DB90-4ECC-B490-552323C225FF HTTP/1.1
2018-05-02 14:47:20:171 - info: [WinAppDriver] [STDOUT] Accept: */*
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] Connection: keep-alive
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] Content-Length: 0
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] Content-Type: application/json
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] Host: 127.0.0.1:4724
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] User-Agent: appium
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:47:20:186 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:47:20:218 - info: [WinAppDriver] [STDOUT] HTTP/1.1 200 OK
2018-05-02 14:47:20:218 - info: [WinAppDriver] [STDOUT] Content-Length: 63
2018-05-02 14:47:20:233 - info: [WinAppDriver] [STDOUT] Content-Type: application/json
2018-05-02 14:47:20:233 - info: [WinAppDriver] [STDOUT] 
2018-05-02 14:47:20:233 - info: [WinAppDriver] [STDOUT] {"sessionId":"6DF966FD-DB90-4ECC-B490-552323C225FF","status":0}
2018-05-02 14:47:20:233 - warn: [WinAppDriver] Did not get confirmation WinAppDriver deleteSession worked; Error was: Error: Did not get a valid response object. Object was: {"sessionId":"6DF966FD-DB90-4ECC-B490-552323C225FF","status":0}
2018-05-02 14:47:20:514 - info: [HTTP] <-- DELETE /wd/hub/session/4b9d3b16-2dd0-4e79-82f8-ce5694e514c6 200 295 ms - 76
〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓
```

WinAppDriverで存在しない要素を取得して404を出した後、Postするまでに10分かかってる・・・
Appiumの問題(設定？)か切り分けるために、Windows Application Driverを直接起動して確かめる。

##### WinAppDriver 起動時の引数

ソース変えないで実行できるように引数で調整。
管理者権限で実行しないとだめってのをどこかで見ました。

```console
"C:\Program Files\Windows Application Driver\WinAppDriver.exe" XXX.XXX.XXX.XXX 4723/wd/hub
```

結果・・・**すぐ終わるやん！**
エラーもNoSuchElementExceptionってちゃんと帰ってくるし、
Appium経由で解決できなければ、WinAppDriverを直接起動した方が良いかなぁ。

</div></details>


**2018/09/13 追記
そして色々やってて新たな困りごとが・・・:weary:**

#### 1つのマシンでWindows Application Driver（WinAppDriver.exe）から複数アプリを起動しているとquitで起動しているアプリすべてのコネクションが切れる

Appiumを経由してWinAppDriver.exeが呼ばれてるんですが、WinAppDriver.exeのプロセスが複数アプリでも1つで、quitが呼ばれるとexeがなくなっているように見えました。

WinAppDriver.exeを直接起動するとquitでもWinAppDriver.exeのプロセスがなくなりません。（それはそれでどうかと思うんですが）
なのでうまく動きます。

closeだとプロセスは残ってもSelenium Gridでドライバが解放されてない扱いになるのでタイムアウトになるまで待つしかなくなります。

AppiumでWinAppDriver.exeを複数プロセスで起動してくれるとか何かしら処置がない限り、1つのマシンで起動できるSessionを1つに制限することにしました。