Docker ComposeでSelenium + Pytestが実行できるブラウザ自動テスト環境を立てる

## サマリ

SeleniumとPytestでブラウザ自動テストをしようとすると、テスト実行をするまでの環境構築がそれなりに面倒。ブラウザを入れ、ブラウザのバージョンに合ったwebdriverを入れ、Pythonを入れ、Pytestを入れ…としていると、インストールだけで数時間近く経ってしまうことも。

そこで今回はDocker Composeを使い、SeleniumとPytestの実行環境立ち上げを簡略化していく。インスタントにブラウザテストの実行環境がほしい場合や、プロジェクト内で実行環境を統一したい場合などに参考にしてほしい。

## システム構成

Docker Composeで以下の2つのコンテナを立ち上げ、コンテナ間をリモートで接続する。

- chrome (Chrome+Selenium実行環境)
- pytest (Pytest実行環境)

chromeコンテナには[selenium/standalone-chrome-debug](https://github.com/SeleniumHQ/docker-selenium)を利用する。これはSelenium公式が提供しているイメージで、その名の通りスタンドアローンでChrome + Seleniumが実行できる。(内部的にはSelenium Grid Hubに1ノードのChromeが接続されている)

pytestコンテナは[Docker公式のpythonイメージ](https://hub.docker.com/_/python)をベースに自分でDockerfileを記述しビルドする。

## ファイル構成

### ローカル作業ディレクトリ

```
docker-pytest
├── Dockerfile_pytest
├── docker-compose.yml
├── screenshots
└── tests
    └── test_google.py
```

### pytestコンテナ内部ディレクトリ

```
/
└── docker-pytest
    ├── screenshots
    └── tests
        └── test_google.py
```

## 各ファイルの中身

### docker-compose.yml

```docker-compose.yml
version: "3"

services:
  chrome:
    image: selenium/standalone-chrome-debug:4.0.0-alpha-7-prerelease-20200826
    container_name: chrome
    volumes:
      - /dev/shm:/dev/shm
    ports:
      - 4444:4444
      - 5900:5900

  pytest:
    build:
      context: ./
      dockerfile: Dockerfile_pytest
    container_name: pytest
    volumes:
      - ./screenshots:/docker-pytest/screenshots/
    tty: true
```

chromeコンテナの記述は[selenium/standalone-chrome-debug](https://github.com/SeleniumHQ/docker-selenium#debugging)に記載されているdockerコマンドをYAML形式とした。

pytestコンテナは[バインドマウント](https://matsuand.github.io/docs.docker.jp.onthefly/storage/bind-mounts/)を利用しており、ローカルの`docker-pytest/screenshots`ディレクトリをpytestコンテナの`/docker-pytest/screenshots`ディレクトリにマウントしている。これでpytestコンテナ内でpytestを実行した際に保存されるスクリーンショットをローカルから参照できるようになる。

### Dockerfile_pytest

テストスクリプト内で日時を利用するのでタイムゾーンだけ変更する。

```Dockerfile
FROM python:3

RUN pip install --upgrade pip && \
    pip install selenium pytest

ENV TZ "Asia/Tokyo"

WORKDIR /docker-pytest
COPY ./tests/ /docker-pytest/tests
```

### test_google.py

ポイントはドライバの指定の仕方。docker-composeするとコンテナ名で名前解決できる。
chromeコンテナ・4444ポートのSelenium Grid Hubに接続するには`http://chrome:4444/wd/hub`に接続すればよい。

```test_google.py
import os
from datetime import datetime

from selenium import webdriver


class TestGoogle:
    @classmethod
    def setup_class(cls):
        cls.driver = webdriver.Remote(
            command_executor="http://chrome:4444/wd/hub",
            options=webdriver.ChromeOptions(),
        )

    def test_access_google(self):
        """
        Googleトップページにアクセスする。

        Googleトップページ(https://www.google.com/)にアクセスする。
        アクセス時にスクリーンショットを撮影し、
        そのファイル名はテストを実行した日時(YYMMDD_hhmmss.png)とする。
        成否は「ブラウザの現在のURLがGoogleトップページになっているか」で判定する。
        """

        driver = TestGoogle.driver

        file_name = datetime.now().strftime("%y%m%d_%H%M%S.png")
        screenshot_path = os.path.join("/docker-pytest/screenshots/", file_name)

        driver.get("https://www.google.com/")
        driver.get_screenshot_as_file(screenshot_path)

        assert driver.current_url == "https://www.google.com/"

    @classmethod
    def teardown_class(cls):
        cls.driver.quit()
```

## 使い方

1. ローカルで`docker-pytest`フォルダ内に移動
2. `docker-compose up -d --build`を実行
3. `docker-compose exec pytest bash`を実行し、pytestコンテナに接続
4. `pytest tests`を実行
5. ローカルの`docker-pytest/screenshots`フォルダにスクリーンショットが生成されていたら完了！

※終了したい時は`docker-compose down`を実行

### 実行の様子の確認

Selenium公式が配布しているDockerイメージには通常版とデバッグ版があり、デバッグ版を利用するとVNC接続をすることでテスト実行している画面をGUIで確認することができる。

接続先は`vnc://localhost:5900`。接続の仕方は下記記事を参照。
[Dockerのselenium/standalone-chrome-debugコンテナにVNC接続する](https://qiita.com/cacarrot/items/d3165056e50850fafef9)

## 今後の展望

今回は1ノードのSelenium Grid Hubであるselenium/standalone-chrome-debugを利用したが、これを複数ノードにすればマルチブラウザテストが実行可能になる。
詳しくは以下を参考に。
[SeleniumHQ/docker-selenium#selenium-grid-hub-and-nodes](https://github.com/SeleniumHQ/docker-selenium#selenium-grid-hub-and-nodes)

## 参考資料

- 書籍
  - [たった1日で基本が身に付く！Docker/Kubernetes超入門](https://gihyo.jp/book/2020/978-4-297-11428-2)
      - Docker初心者だったので非常に助けになった。良書。
- docker-compose.yml, Dockerfileを書くにあたって参考にした記事
  - [Compose ファイル バージョン 3 リファレンス](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/)
  - [docker-compose up したコンテナを起動させ続ける方法](https://qiita.com/sekitaka_1214/items/2af73d5dc56c6af8a167)
  - [Dockerコンテナのタイムゾーン変更方法](https://qiita.com/rururu_kenken/items/972314402d588e073d40)