Appiumでのテストで機械学習を使ってみた

#Appiumを使った自動テストでCNNを利用してみた。

- 状況:　Winodows上のアプリをテストするため、Appium+Seleniumでテストランナーを作って走らせたが、テスト対象側がテストシナリオに沿って動かないとテストが止まってしまう(ランナーは仕事をやった気になって終わっている)。
- やりたかったこと：ランナーが動作指示に対してテスト対象の画面応答をキャッチして次のテストステップに遷移させる(継続、中断、メールで通知など：自分はしないがランナーには**ホウレンソウ**を強要)。

- 対応：テスト対象の画面をappiumでキャプチャー、CNNで画面認識させた結果で次ステップを決めるようにした。

### 画面キャプチャ。
まずはデータ収集のためのキャプチャツールを用意.
[前回記事](https://qiita.com/K-029/items/7d767086531ce862bafa)を使ってテスト対象(ここでは前回同様ワードパット)をキャプチャーする。
実際はテスト対象を動作させながら　save_screenshot() をループで繰り返して画像を貯めた。

```python:caputure.py
# -*- coding: utf-8 -*-
import cv2
from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

　~ 前回記事で書いた関数は省略~

if __name__ == '__main__':
  #Windows Appliction Driverを起動 .
  #ワードパッドを起動しておく.
  appdriver = SetUp_SelectClassName('WordPadClass')

  #Capture Recognition
  filepath = "screenshot.png"
  
  #Captureしてファイルの保存.
  appdriver.save_screenshot(filepath)
  #画像を呼び出して確認する.
  img = cv2.imread(filepath)
  cv2.imshow('sample', img)
  cv2.waitKey(0)
  cv2.destroyAllWindows()
```

### CNN.
分類器にはCNNを採用、CNNを選んだ理由は特になく、集めた画面をフォルダ分けするだけで願いがかないそうだったから。
CNNについては[リンク先](https://www.tensorflow.org/tutorials/images/classification)コードを二値分類:**binary**'を'カテゴリー:**categorical**'に変更して学習器を作成。
下のコードではテスト対象が"1","2","None"の3パターンの画面を表示する可能性があるとして3画面を分類させている。
キャプチャした画面を分類したいカテゴリー**"1"**,**"2"**,**"None"**のフォルダを作成して分ければ学習済みデータが作成できます。

```python:TestCategory.py
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':
 PATH = "C:\\Users\Data"
    train_dir = os.path.join(PATH, 'train')
    validation_dir = os.path.join(PATH, 'validation')
    # '1','2','None'のカテゴリー.
    Categrylist = ['1','2','None']
    total_train = 0
    for f in Categrylist:
        total_train += FileNum(train_dir,f)
    total_val = 0
    for f in Categrylist:
        total_val += FileNum(validation_dir,f)

    #画像ファイル数の確認
    print("-----------------------------------")
    #ハイパーパラメータ
    batch_size = 3
    epochs = 8
    IMG_HEIGHT = 150
    IMG_WIDTH = 150
    #PriProcess
    train_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our training data
    validation_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our validation data
    train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
                                                           directory=train_dir,
                                                           shuffle=True,
                                                           target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                           class_mode='categorical')
    val_data_gen = validation_image_generator.flow_from_directory(batch_size=batch_size,
                                                              directory=validation_dir,
                                                              target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                              class_mode='categorical')
    #割り振られてラベルを確認する.
    print("Category : Label")
    print(train_data_gen.class_indices)
    print("-----------------------------------")
    #モデルの組み立て
    model = Sequential([
        Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
        MaxPooling2D(),
        Conv2D(32, 3, padding='same', activation='relu'),
        MaxPooling2D(),
        Conv2D(64, 3, padding='same', activation='relu'),
        MaxPooling2D(),
        Flatten(),
        Dense(512, activation='relu'),
        Dense(3, activation='softmax')  #Multicategoryは出力がClass数　活性化関数はSoftMax
        ])

    #コンパイル
    model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
    #学習
    history = model.fit_generator(
        train_data_gen,
        steps_per_epoch=total_train // batch_size,
        epochs=epochs,
        validation_data=val_data_gen,
        validation_steps=total_val // batch_size
        )

    #モデルの保存.
    model.save('Category_sample.h5')
```
### ランナーでの画面認識。
ここでも例題は"ワードパット"です。
学習済みモデルができたらテストランナーでキャプチャー画面を分類させて利用。
テストランナーでの実装は大したもではなく"モデル読み込み"、"キャプチャ"、"予測"、"結果に対する処理"を記載するだけです。
これで自動テストでテスト対象とインタラクティブにやり取りできます。

```python:TestRunnner.py
from appium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

import numpy as np
import tensorflow as tf 

　~ 前回記事で書いた関数は省略~

if __name__ == '__main__':
  model =  tf.keras.models.load_model('Category_sample.h5')
  #ワードパッドを起動しておく. 
  appdriver = SetUp_SelectClassName('WordPadClass')
  
  #Screen Shot
  filepath = "screenshot.png"
  appdriver.save_screenshot(filepath)   
  Image = tf.keras.preprocessing.image.load_img(filepath,target_size=(150,150))
  
  #Image change to arry.
  input_arr = tf.keras.preprocessing.image.img_to_array(Image)
  input_arr = np.array([input_arr])  # Convert single image to a batch.
  predictions = model.predict(input_arr)
  print(predictions)
  #predictionでランナー処理を切り替えるようにする.

```
## 終わりに
この記事を書く際に調べたら目的は違えど同じようなことを試みている(見た)報告がいろいろありましたので"Appium","AI"あたりで検索してみることをお勧めします。 

コードは部分的に記述してあるので [前回記事](https://qiita.com/K-029/items/7d767086531ce862bafa),[リンク先](https://www.tensorflow.org/tutorials/images/classification)をみて間を埋めてもらえれば動作します。