PyAutoGUI(一部Selenium)+pytestで、あらゆるPCアプリのE2Eテストを自動化してみた(い)


とりあえずタイトル詐欺にならないよう、末尾は「い」にしときます(^_^;)。

正確には、「頑張れば、あらゆるPCアプリのE2Eテストを自動化できそうなことまでは分かった」というのが、この記事の趣旨です。


E2Eテストとは?

@NAKKA-Kさんが簡潔に素晴らしくまとめてくださっているので、リンクにて説明に変えさせていただきたく。

E2Eテスト(インテグレーションテスト)の利点と不利点
https://qiita.com/NAKKA-K/items/58d6b8476a3717179bda


題材とするアプリケーション

Chromeを自動操作してHerokuの管理画面にログインし、課金状況を確認するというものです。

課金状況を確認するところまでは、まだできてないんですが。


今回、テスト自動化してみる箇所

まずは最低限ですが、Herokuにログインできたところのテストの自動化をご紹介してみます。


セットアップ

Python 3.7.0 on Windowsで動作確認してます。

pip install PyAutoGUI
pip install chromedriver-binary
pip install selenium
pip install pytest
pip install python-dotenv

ライブラリそれぞれのご紹介と、今回ご紹介するプログラムでの役割

テストスクリプトで使用しているライブラリが、pytestとPyAutoGUIです。

  • pytest
    • Pythonで自動テストスクリプトを書くためのフレームワークです。
    • Python標準のunittestと比較して、テストに失敗した原因が分かりやすい……らしいです。
    • 個人的には、まだあまり使い込めていません。
    • 今回は、自動テスト自体を、pytestを使って行います。

  • PyAutoGUI
    • キーボード入力やマウス操作などのGUI操作を自動化するスクリプトをPythonで書ける、簡易RPAです。
      テキストボックスやボタンの検知は、画像マッチングで行います。
    • 簡潔にまとまった良記事
    • 今回は、ログインに成功したこと = テストOKであったことの検証を、
      PyAutoGUIの画像マッチングの機能を使って行います。

題材アプリケーションで使用しているライブラリが、Selenium・ChromeDriver・python-dotenvです。


  • Selenium
    • スクリプト言語で、ブラウザを自動操作することができるライブラリです。
    • Selenium IDEというChrome・Firefoxのアドオンを使えば、手動でのブラウザ操作の記録も行えるそうです(未検証)。
    • 参考
    • Selenium について
    • Selenium クイックリファレンスより、Selenium API(逆引き)
    • Python以外にも、Java・Rubyといった言語からも使うことができます。
    • 今回は、ブラウザからHerokuの管理画面にログインする操作の自動化を、Seleniumのライブラリを使って行います。

  • ChromeDriver
    • SeleniumでChromeの操作を自動化するためのライブラリです。

  • python-dotenv
    • Webサービスにログオンしたり、WebAPIを呼び出したりするプログラムを書く場合、ID・パスワード・APIキーなどをソースコード内に直接書いて、そのままソースをGitHubの公開リポジトリにpushしてしまったりすると大変なことになります。
      python-dotenvを使う際は、.envという外部ファイルにID・パスワード・APIキーなどを書いておきます。
      そして、.envファイルはリポジトリにコミットしないようにすることで、ID・パスワード・APIキーなどの記述を、プログラムソースから分離します。
      そしてプログラム実行時にpython-dotenvが、.envに書いておいた値を環境変数として読み込んで使用することで、外部公開したくないデータをプログラムから安全に使うことが可能になります。
    • 元はRubyのgem(= Rubyライブラリ)として開発されたDotenvのPython移植版のようです。
    • 今回は、HerokuのID・パスワードをプログラムソース内に書かないようにするために使用しています。

今回組んでみたテストスクリプト

test_loginHeroku.py
from time import sleep
from loginHeroku import HerokuAdmin
import pyautogui

def found_screen(fileName):
    return (pyautogui.locateCenterOnScreen(fileName) is not None)

def test_loginHeroku():
    herokuAdmin = HerokuAdmin()

    sleep(5)
    assert found_screen('success_icon.png')

テストスクリプトの説明

  • 「test_loginHeroku」が、pytestが実行してくれるテストスクリプトのメソッドです。
    • 「HerokuAdmin」クラスが、今回の題材アプリケーションである、Herokuの管理画面に自動ログインするプログラムの本体です。
    • 「assert」は、Python標準のステートメントです。
    • 「結果がTrueになればテストOK」となるように、テストスクリプトを書いていきます。
    • 今回は、found_screenメソッドの引数に、Herokuにログインできた画面の右上に表示される忍者っぽい?アイコンの画像を渡しています。

↑ こいつね。

題材のアプリケーションを実行した後、「このアイコンが画面内に表示されていれば」テストOK、とみなします。


テストスクリプトの説明(続き)

  • 「found_screen」が、引数にファイル名を与えた画像が、その時点の画面内にマッチするか、を検証するメソッドです。
    • PyAutoGUIのlocateCenterOnScreenメソッドを使っています。
      これは、「特定の画像ファイルとマッチングするウィンドウ内座標(の中央)」を返すメソッドです。ウィンドウ全体に、画像とマッチングする領域が無ければNoneを返します。
      今回はfound_screenメソッドは、「引数にファイル名を与えた画像がマッチングした場合にTrueを返す」ようにしたかったので、このように記述しています。

実行結果

$ pytest .\test_loginHeroku.py
============================================================================================== test session starts ==============================================================================================
platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\Chihiro\pyautogui\heroku-monitor
collecting ...
DevTools listening on ws://127.0.0.1:53182/devtools/browser/5720bd09-da58-48bb-85da-3f963743ad9e
collected 1 item

test_loginHeroku.py .                                                                                                                                                                                      [100%]

=========================================================================================== 1 passed in 28.36 seconds ===========================================================================================

期待通りにテストが通りました。

この調子でテストケースを増やしていけば、様々なE2Eテストが自動化できそうです。


感じたこと

テストスクリプトをメンテナンスしやすくするために、テストケースをこまめに区切るのが良さそう

一般的に、E2Eテストは不安定な(= テストコードを書いた時にはうまく通っても、その後、常にうまく通ってくれるとは限らない)傾向があります。

特に今回の題材アプリケーションのように、外部サイトにアクセスするようなプログラムの場合、ネットワーク状況や外部サイトの負荷状況、レイアウト変更等、様々な要素でテストが失敗し得ます。

そのような場合にどこでテストが失敗したかを分かりやすくするためにも、テストケースは小さく区切っておくことが重要だと感じました。


テストスクリプトの中で、Webアプリを自動操作することも可能なのだろう

今回は、「Herokuの管理画面に自動でログインする」プログラムのテスト自動化が題材でした。

が、テストを自動化したいアプリが、自動ログインする先のWebアプリの方であれば(というよりも、その方が一般的だと思いますが)、PyAutoGUIやSeleniumによるWebアプリの操作を、テストスクリプト内で行うことになると思います。

次の機会には、こちらのテストスクリプト記述を試してみたいと思います。


以上です

最後までお読みいただき、ありがとうございました。

良いPythonライフを!