Androidテスト自動化のためにappiumを導入(windows+python)

Android+windows+python環境でappiumの導入方法を調べたメモ。
pythonを使うのは[公式](http://appium.io/docs/en/about-appium/getting-started/?lang=ja#installing-appium)のjsのコードが動かなかったという理由もある。
大筋は[Get started with mobile automation: Appium & Python](https://qxf2.com/blog/appium-mobile-automation/)の要約。

##準備
AndroidStudioやpython, Node.jsなどは既にインストール済み。

###1. appium
GUIとCLIがある。筆者はCLIを使用した。
導入方法は[こちらの記事](https://qiita.com/emurin/items/fe640327f847c474f1e8)が詳しい
本体

```
npm install -g appium
```

インストールに不備がないか診断するツール

```
npm install -g appium-doctor
```

診断する

```
appium-doctor --android
```
>Bin directory for %JAVA_HOME% is not set

と言われたので`%JAVA_HOME%\bin;`をpathに追加

###2. python クライアントライブラリ
pythonを使うつもりなので下記でインストール
`pip install Appium-Python-Client`

###3. Android Virtual Deviceを作って起動
Android StudioからAVDマネージャを起動して仮想デバイスを作る。下記のような設定を使った。
端末: Nexus One 
APILevel: 26
ABI: x86
target:Android8.0 (Google APIs)

###4. appium起動
**2019/1/14追記　管理者権限で起動する必要があるかもしれない**

```
appium
```

##テスト
###1. 対象のapkを準備
元記事ではテスト対象に[Chess Free](https://play.google.com/store/apps/details?id=uk.co.aifactory.chessfree&hl=en)というアプリ使っている。[ここ](https://www.appsapk.com/chess-free/)から.apkをダウンロードして適当な位置に置く。

###2. AndroidManifest.xmlから必要な情報を得る
必要なのは、
1. パッケージ名
2. 起動するアクティビティ名

apkのルートディレクトリにあるAndroidManifest.xmlから得る。またはコマンドプロンプトから
`aapt dump badging apkへのパス`
でも情報が出るらしい（筆者はアクティビティ名を見つけられなかった）

Chess Freeに関しては
>package="uk.co.aifactory.chessfree" and 
android:name=".ChessFreeActivity"

が必要な情報。


###3. テストを書く

```python:android_chess.py
"""
Qxf2: Example script to run one test against the Chess Free app using Appium
The test will:
- launch the app
- click the 'PLAY!' button
"""
 
import os
import unittest
from appium import webdriver
from time import sleep
 
class ChessAndroidTests(unittest.TestCase):
    "Class to run tests against the Chess Free app"
    def setUp(self):
        "Setup for the test"
        desired_caps = {}
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '8.0'
        desired_caps['deviceName'] = 'Nexus'
        # Returns abs path relative to this file and not cwd
        desired_caps['app'] = os.path.abspath(os.path.join(os.path.dirname(__file__),'apps/Chess Free.apk'))
        desired_caps['appPackage'] = 'uk.co.aifactory.chessfree'
        desired_caps['appActivity'] = '.ChessFreeActivity'
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
 
    def tearDown(self):
        "Tear down the test"
        self.driver.quit()
 
    def test_single_player_mode(self):
        "Test the Chess app launches correctly and click on Play button"
        sleep(15)
        element = self.driver.find_element_by_id("uk.co.aifactory.chessfree:id/ButtonPlay")
        element.click()
        sleep(5)
 
#---START OF SCRIPT
if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(ChessAndroidTests)
    unittest.TextTestRunner(verbosity=2).run(suite)
```

`setUp()`内の設定が自分の環境の設定と一致するように注意する。
元記事とは異なり、Chess Freeのアプリ内で下記画像のような確認画面がでるので
![chess1.png](https://qiita-image-store.s3.amazonaws.com/0/296108/87a6da99-c7e1-a090-c84c-b12e90ddc446.png)
とりあえず`sleep(15)`しているあいだに"Accept"を手動で押すようにした
自分のアプリをテストするならボタンの識別名等はわかっているが、
他人のアプリのボタンidを知る方法はあるのだろうか？

**2019/4/7追記**
Android Studioに付属する**UI Automator Viewer**で可能とのこと（@jwoff25 さんにコメントで紹介いただきました）。また、AppiumのGUI版でも可能。


###4. 実行
AVDおよびappiumが起動しているので、あとは実行だけ
`python android_chess.py`

ChessFreeの"PLAY!"ボタンが押され、その後アプリが終了する
コマンドプロンプト画面はこんな感じになる。

>test_single_player_mode (\__main__.ChessAndroidTests)
Test the Chess app launches correctly and click on Play button ... ok
\----------------------------------------------------------------------
Ran 1 test in 29.538s
OK