概要
Docker + Selenium + Headless Chrome な環境で feature spec を実行する時に、ブラウザの開発者ツールを使ってデバッグしたかったので調べた。
こんな感じで使える↓
サンプルコードはこちら
https://github.com/yszk0123/selenium-docker-rails-example
今回の例では Chrome を使ったが、Firefox も可能なはず。
検証環境
- macOS (High Sierra 10.13.3)
- Docker for Mac (18.02.0-ce-mac53)
- Docker
- ruby (2.5.0-alpine)
- selenium/standalone-chrome-debug (3.9.1-actinium)
- Gem
- rails (5.1.5)
- rspec-rails (3.7.2)
- capybara (2.18.0)
やり方
ブラウザ・Selenium サーバー・VNC サーバーなどが同梱されたselenium/standalone-chrome-debug という docker イメージを使う1。
Docker for Mac には
VNC クライアントが同梱されているので2、特別な設定なしにコンテナの外からブラウザを操作できる。
今回は次の2つのコンテナに分ける。
- テストを実行するコンテナ
app
- Selenium サーバー用のコンテナ
chrome
また、テスト用に以下のホスト・ポートを使用する (docker-compose のネットワーク機能を活用)。
- アプリケーションサーバー
http://app.com:3000
- Selenium サーバー
http://chrome.com:4444
- VNC サーバー
http://localhost:5900
(ホストマシンからアクセス)
docker-compose の設定
version: "2"
services:
# テストを実行する Rails サーバー。Dockerfile は以下のリンク
# https://github.com/yszk0123/selenium-docker-rails-example/blob/master/Dockerfile
app:
container_name: app
build: .
ports:
- 3000:3000
volumes:
- .:/app
networks:
test-network:
aliases:
- app.com
chrome:
container_name: chrome
image: selenium/standalone-chrome-debug:3.9.1-actinium
ports:
- 4444:4444
- 5900:5900
networks:
test-network:
aliases:
- chrome.com
networks:
test-network:
RSpec (Capybara) の設定
require 'rspec/rails'
require "capybara/rails"
require "selenium/webdriver"
if ENV["LAUNCH_BROWSER"]
Capybara.configure do |config|
# docker-compose で設定した alias を使い
# chrome コンテナ側から app コンテナ内のサーバーを参照
config.server_host = "app.com"
# ポートはデフォルトではランダムに割り当てられるが、設定を簡単にするため固定
config.server_port = 3000
config.javascript_driver = :selenium_chrome_headless
end
Capybara.register_driver :selenium_chrome_headless do |app|
Capybara::Selenium::Driver.new(
app,
# 別コンテナで動かしている Selenium のリモートサーバーを操作
browser: :remote,
# Chrome 以外は適宜設定を変える
desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
args: [
"window-size=1024,512",
]
}
),
url: "http://chrome.com:4444/wd/hub",
)
end
end
実行方法
$ docker-compose up -d
# VNC クライアントを起動
$ open vnc://localhost:5900
# ここでパスワードを求められるので secret と入力
# テストを実行
$ docker exec -it app sh -c 'LAUNCH_BROWSER=true bundle exec rspec'
後は止めたいところに pry.binding
などを仕込めばOK。
require "rails_helper"
RSpec.feature "Sample", js: true do
scenario "sample" do
visit sample_path
binding.pry
expect(page).to have_content("Hello, world")
end
end
おまけ (JavaScript でテストを書く場合)
サーバーサイドは Rails だけど JavaScript でテストを書くケースもありそうだったので軽く調べた。
以下は selenium-webdriver
を使った例。
$ docker-compose run --rm app sh -c 'bin/rails s'
$ docker-compose run --rm chrome
$ docker-compose run --rm node
+ node:
+ container_name: node-test
+ image: node:9.6.1-alpine
+ command: sh -c 'cd /app && yarn init -y && yarn add selenium-webdriver && node test.js'
+ volumes:
+ - ./test.js:/app/test.js
+ networks:
+ test-network:
chrome:
container_name: chrome
const { Builder } = require('selenium-webdriver');
function wait(delay) {
return new Promise(resolve => setTimeout(resolve, delay));
}
(async () => {
const driver = new Builder()
.forBrowser('chrome')
.usingServer('http://chrome.com:4444/wd/hub')
.build();
try {
await driver.get('http://app.com:3000/sample');
await wait(3000);
} finally {
await driver.quit();
}
})();
おわりに
Rails と Selenium にはあまり詳しくないため、間違いがあればご指摘いただけると助かります。