皆さんこんにちは、4月で入社2年目になりました。
IoTインテグレーション事業部 開発部の阿部です。
本日は業務の中で、DockerとSelenium Gridを使用して自動クロスブラウザテストの環境構築をしたのでご紹介させていただきます。
ブラウザ上でのwebページ操作をスクリプトから行うためのツールであるSeleniumとSeleniumで実行される動作を管理するツールであるSelenium Gridを利用し、一つのテストスクリプトで複数のブラウザを自動でテストできるようにします。
Selenium Grid とは
複数マシンでテストを並行して実行し、様々なブラウザのバージョンとブラウザ構成を一元管理するツールです。
流れとしては、テストを実行したいブラウザを指定して、テストスクリプトを実行すると、hubがルーティングすることで登録されているnodeの中から適切なnodeに対して処理を振り分けます。
今回はDockerで以下の構成図のように構築していきます。
- 自動テストスクリプトを実行するコンテナ(3)
- hubを起動するコンテナ(1)
- nodeを起動するコンテナ(3)
開発環境
WSL2を導入してその上にUbuntuをインストールし、Docker環境を構築してあります。
※Ubuntu上に入っているdockerとdocker-composeのバージョンは、公式ドキュメントに書かれている手順で更新してあります。
- Ubuntu : 20.04
- Docker Desktop:3.3.1
- Docker Engine : 20.10.6
- Docker-compose : 1.29.1
- Python:3.8
ディレクトリ構成
1 2 3 4 5 6 7 8 9 10 |
. ├─ app ├─ screenshot (テストスクリプト実行後に生成) ├─ chrome ├─ firefox └─ edge ├─ Dockerfile ├─ requirements.txt └─ browser_test.py └─ docker-compose.yml |
テストスクリプト作成
テストスクリプト(browser_test.py)はハブに接続して、ヘッドレスモードでブラウザを操作する処理をPythonで記述します。今回はエコモットのホームページにアクセスしたいと思います。
テストするブラウザによってRemote WebDriverのオプションを変更する必要があり、コンテナに設定する環境変数を利用して変更します。また、テストのエビデンスを残すために、テスト実行後の画面をスクリーンショットをして保存するフォルダを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
from selenium import webdriver import os import json # コンテナに登録した環境変数の設定 BROWSER_NAME = os.environ['BROWSER_NAME'] HOST_NAME = os.environ['HUB_HOST'] # スクリーンショットを保存するためのパスをブラウザごとに指定 BROWSER_FILE_PATH = "screenshot/" + BROWSER_NAME + "/" CURRENT_DIR_PATH = os.path.dirname(os.path.abspath(__file__)) # テスト時のスクリーンショットを保存するためフォルダを作成 if not os.path.exists(os.path.join(CURRENT_DIR_PATH, BROWSER_FILE_PATH)): os.makedirs(os.path.join(CURRENT_DIR_PATH, BROWSER_FILE_PATH)) # テストするブラウザごとにwebdriverのオプションを設定 if BROWSER_NAME == "chrome": options = webdriver.ChromeOptions() elif BROWSER_NAME == "firefox": options = webdriver.FirefoxOptions() else: options = webdriver.EdgeOptions() options.use_chromium = True print(BROWSER_NAME) # 画面を表示しないのであれば、ヘッドレスオプションを付ける options.add_argument('--headless') options.add_argument('--window-size=1280,1024') # Selenium hub Serverに接続する with webdriver.Remote( command_executor=f'http://{HOST_NAME}:4444/wd/hub', desired_capabilities=options.to_capabilities(), options=options, ) as driver: # ブラウザを操作する driver.get('https://www.ecomott.co.jp/') print(driver.current_url) # スクリーンショットを取って保存 driver.save_screenshot(os.path.join(CURRENT_DIR_PATH, BROWSER_FILE_PATH + "test.png")) |
Dockerfile作成
Selenium実行環境を用意するためにPythonのベースイメージを使用して、Seleniumをインストールします。
1 2 3 4 5 6 7 8 9 10 |
FROM python:3.8 RUN apt-get -y update && \ python -m pip install --upgrade pip ADD requirements.txt . RUN pip install -r requirements.txt WORKDIR /app |
reqirements.txt作成
自動テストの様子をGUIで確認する場合は、VNC接続が必要になります。Edgeには用意されていませんが、ChromeとFirefoxにはVNCサーバーがインストールされているイメージが用意されているのでGUIで動作を確認することができます。しかし、VNCサーバーがインストールされているイメージはSeleniumのバージョンが3系でないと対応していないため、デバッグを行う際はSeleniumの3系をインストールしてバージョンを合わせる必要があります。動作確認をする際のviewerは、VNCviewerやUltraVNCなどを利用します。
1 2 3 |
selenium==4.0.0-beta-3 # GUIで確認する時はselenium3を利用する(VNC接続用) # selenium |
docker-compose.yml作成
Selenium実行環境と、Seleniumのハブ、ブラウザを起動するノードのコンテナを記述していきます。ハブとノードは、Seleniumが展開している公式のDockerイメージを使うと簡単に構築できます。
https://github.com/SeleniumHQ/docker-selenium
ノードのイメージは、既にブラウザとWebDriverがインストールされているので、ローカルで構築するときにバージョンを合わせないと動作しないという手間を省けます。
今回はChromeとFirefox、Edgeでテストをするために、Selenium実行環境とノードコンテナをそれぞれ3つ用意し、ハブと合わせて合計7つのコンテナを立ち上げます。また、VNC接続をしてデバッグを行う際は、ノードコンテナでVNC接続用ポート(5900)を解放する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
version: '3.8' services: selenium-hub: image: selenium/hub:4.0.0 # VNC接続用 # image: selenium/hub:3.141 ports: - 4444:4444 - 4442:4442 - 4443:4443 node-chrome: image: selenium/node-chrome:4.0.0 # VNC接続用 # image: selenium/node-chrome-debug:3.141 # ports: # - 5900:5900 volumes: - /dev/shm:/dev/shm depends_on: - selenium-hub environment: - TZ=Asia/Tokyo - HUB_HOST=selenium-hub # hubとの接続で必要 - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 # 同時実行数を増やす場合に設定を行う # - SE_NODE_MAX_SESSIONS=2 # - SE_NODE_OVERRIDE_MAX_SESSIONS=true node-firefox: image: selenium/node-firefox:4.0.0 # VNC接続用 # image: selenium/node-firefox-debug:3.141 # ports: # - 5901:5900 volumes: - /dev/shm:/dev/shm depends_on: - selenium-hub environment: - TZ=Asia/Tokyo - HUB_HOST=selenium-hub - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 node-edge: image: selenium/node-edge:4.0.0 volumes: - /dev/shm:/dev/shm depends_on: - selenium-hub environment: - TZ=Asia/Tokyo - HUB_HOST=selenium-hub - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 python-selenium-for-chrome: build: ./app image: python/selenium_chrome # VNC接続用 # image: python/selenium_chrome:debug volumes: - ./app:/app depends_on: - selenium-hub tty: true environment: - HUB_HOST=selenium-hub - BROWSER_NAME=chrome # コンテナ初回起動時にテスト実行 # entrypoint: ./entry_point.sh python-selenium-for-firefox: build: ./app image: python/selenium_firefox # VMC接続用 # image: python/selenium_firefox:debug volumes: - ./app:/app depends_on: - selenium-hub tty: true environment: - HUB_HOST=selenium-hub - BROWSER_NAME=firefox # entrypoint: ./entry_point.sh python-selenium-for-edge: build: ./app image: python/selenium_edge volumes: - ./app:/app depends_on: - selenium-hub tty: true environment: - HUB_HOST=selenium-hub - BROWSER_NAME=edge # entrypoint: ./entry_point.sh |
イメージ作成
作成したDockerfile, requirement.txt, docker-compose.ymlをもとにイメージをビルドします。
1 |
$ docker-compose build |
コンテナ起動
作成したイメージをもとにコンテナを作成して起動します。
1 |
$ docker-compose up -d |
テスト実行
立ち上がったSelenium実行環境のコンテナにシェルをアタッチし、Pythonを実行します。
1 2 3 |
$ docker exec -it [コンテナ名 or コンテナID] bash /app# python browser_test.py |
以下のようなスクリーンショットを取ることができました。
Chrome
Firefox
Edge
また、毎回シェルをアタッチしてテストを実行するのは手間がかかり、コンテナ起動時にテストを自動実行させたいという場合もあると思います。
その場合はdocker-compose.ymlでSelenium実行環境の記述にentrypointを追加します。
1 |
entrypoint: ./entry_point.sh |
シェルスクリプト(enrty_point.sh)の内容は以下のようになります。ここでハマったのですが、コンテナ起動直後にテストスクリプトを実行するとハブとノードが接続完了していない状態で処理が走り、エラーが出てしまうので、接続待機時間を設けるようにします。
1 2 3 4 5 6 7 8 |
#!/bin/sh # selenium hubとnodeの接続待機時間 sleep 10s echo '========== test start ==========' python browser_test.py echo '========== test finish ==========' |
テストが終了していることを確認したら、コンテナを終了し、削除します。
1 |
$ docker-compose down |
さいごに
自動化や属人性を排除するシステムを構築をすることが好きなのでとても楽しかったです。さらに発展させて、AWSを利用してコードをpushしたらクロスブラウザテストを自動で実行するようなCI環境を構築したいと思います。
参考資料
seleniumgrid 公式ドキュメント
https://www.selenium.dev/docmentation/en/grid/
Web UIテスト自動化の実行環境をSelenium Gridで
https://techblog.zozo.com/entry/qa-webui-test-automation-01