Seleniumを実行するAPIサーバーを作成する

皆さんこんにちは、Optimal BizのAndroid 開発チームの山口です。 最近暑すぎてエアコンがないとリモートワークに身が入らない日々を過ごしています。 今回はプロダクトのテスト環境を良くするための下調べとして、簡易的なテスト環境を作成したので紹介していきたいと思います。

背景

Optimal Biz、Android開発チームではリグレッションテストをAppiumで実行しています。

(Appiumの導入や利用方法はこちらをどうぞ!)

Optimal Bizは管理サイトからの指示を受けることによって、割り当てる設定を切り替えることができますが、現在はこの設定を切り替えるWeb側の動作を、テストを実行する端末のブラウザで行っているため、操作が不安定となっているのが現状です。

この問題を解決するため、管理サイトでの操作を既存の自動テストから完全に切り離し、別のPCで操作を行ってもらうようにします。

また、管理サイトの動作を今後自動化しやすくするため、管理サイトを動かすためのSeleniumサーバーを作っていきます。

selenium-docker

Seleniumを動かすためには、ブラウザとブラウザを動かすためのWebDriver、seleniumを動かすためのツールをインストールする必要があります。環境をわざわざ構築するのは面倒なため、dockerを使って実行環境を作成していきます。Seleniumは公式でDockerイメージを提供しているので、今回はそちらを利用していきます。

selenium-dockerを試す

今回はPythonを使用して、selenium-dockerを試していきます。 サンプルコードは下記の通り。

from selenium import webdriver

# Firefox のオプションを設定する
# (現在Windows(WSL2)のdockerだとchromeが使えないです)
options = webdriver.FirefoxOptions()

# Selenium Server に接続する
driver = webdriver.Remote(
    command_executor='http://localhost:4444/wd/hub',
    options=options,
)

# Selenium 経由でブラウザを操作する
driver.get('https://www.optim.co.jp/')
print(driver.current_url)

# ブラウザを終了する
driver.quit()

selenium-dockerのコンテナに接続して、コンテナ内のブラウザでオプティムの公式サイトにアクセスして、URLを表示するという動作ができます。 他の操作方法についてはSeleniumの公式サイトを確認してみてください

FlaskでAPIサーバーを構成する

今回はDBなどの構築は行わないため、数行でAPIサーバーを構築できるFlaskを利用します。 サンプルコードは下記の通り。

import flask

app = flask.Flask(__name__)

@app.route("/sample", methods=["POST"])
def sample():
    response = {
        "success": False
    }
    # sampleにPOSTされていることを確認
    if flask.request.method == "POST":
        # POSTされたJSONを取得
        json = flask.request.get_json()
        print json
        # 返すJSONの値を更新
        response["success"] = True
    # 辞書型をJSONに変換して呼び出し元に送信
    return flask.jsonify(response)


if __name__ == "__main__":
    print(" * Flask starting server...")
    app.run()

これでサーバー(/sample)に対してPOSTを要求したら、json形式のデータが返ってくるサーバーができました。

APIサーバーの構成

今回はAPIサーバーをたてて、テストコードに必要な情報をPOSTされたら、その設定値に応じて、テストコードを動かしていく様にします。簡単な構成は下記の通りに設定します。またテスト内容に応じて、サンプルコードを編集します。

f:id:optim-tech:20210616092302p:plain

  • docker-compose.yml
version: "3"
networks:
    app_net:
      driver: bridge
      ipam:
        driver: default
        config:
          - subnet: 192.168.200.0/24
            gateway: 192.168.200.1
services:
  firefox_driver:
    image: selenium/standalone-firefox-debug
    ports:
      - '5900:5900'
    networks:
        app_net:
          ipv4_address: 192.168.200.11
  flask_api:
    build: .
    ports:
      - '8000:8000'
    volumes:
      - ./flask-server:/flask-server
    networks:
        app_net:
          ipv4_address: 192.168.200.12
    command: >
      sh -c "python run_server.py"

自動テストコードで実際にAPIを叩いてみる

最後に今まで使用していた端末のテストコードに今回、構築したサーバーに対してAPIを叩くメソッドを追加していきます。 サンプルコードは下記の通り。(既存のテストコードはrubyで実装しているため、rubyのメソッドコードになります。)

require 'net/https'

# uri -> ex:) http://localhost:8000/sample
# dict_value -> 辞書形式のデータ
def api_post(uri, dict_value)
    # サーバーURLをパースさせる
    uri = URI.parse(uri)
    # サーバーから返される値
    response = nil

    # request を作成する
    request = Net::HTTP::Post.new(uri.request_uri, initheader = {'Content-Type' =>'application/json'})
    request.body = dict_value.to_json

    # 通信先を指定
    http = Net::HTTP.new(uri.host, uri.port)
    # httpで通信を行う
    http.use_ssl = false
    # 証明書を使わない様に設定
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE

    # 正しくAPIが叩かれているか確認
    # http.set_debug_output $stderr

    # サーバーにPOSTする
    http.start do |h|
        response = h.request(request)
    end
    return response
end

selenium-dockerの内部の様子

  • サンプルコードにテストコードを追加した後のselenium-dockerの内部の様子(vncを利用して確認することができます) f:id:optim-tech:20210616105425g:plain

終わりに

今回はselenium-dockerを利用して、できることの確認という感じで実装を進めていましたが、今後はWebアプリの操作で自動化させたいものをこのようなサーバーで一元管理できれば、作業効率が上がっていきそうですね!

オプティムでは開発環境を改善していきたいエンジニアを募集しています。