RSpec の Feature Spec を System Spec に置き換えた
今回は、playwright-ruby-client を導入した際の手順を残したいと思います。
弊社ではこれまで E2E テストを RSpec + Capybara + Selenium で行なっていました。しかし、この構成では Flaky の発生頻度が高く実行時間も非常に長かったため、CI 高速化の取り組みの一つとして RSpec + Capybara + playwright-ruby-client + Playwright にリプレースを行いました。
Playwright には以下の利点があり非常に使いやすいです。
- クロスブラウザテストのサポート: PlaywrightはChromium、Firefox、WebKitなど複数のブラウザをサポートしており、単一のAPIでクロスブラウザテストを実行できます。
- 自動化機能の充実: Playwrightは、ユーザーの動きに近い形でテストケースを自動生成する機能を持っています。
- 高速なテスト実行: Playwrightはブラウザを並列で実行できるため、テストの待ち時間が短縮されます。また、テスト項目ごとにブラウザが初期化されるため、一貫した環境でのテスト結果が保証されます。
- 詳細なログとデバッグ機能: テストが失敗した場合には、動画やスクリーンショット、DOM状態など詳細なログが生成されるため、デバッグが容易です。
- CI/CDパイプラインへの統合: PlaywrightはCIツールとの統合が容易であり、GitHub ActionsやJenkinsなどで簡単に使用できます。
- 豊富な機能セット: スクリーンショットやビデオキャプチャ、ネットワークトラフィックのインターセプトなど、多様な機能を提供しており、さまざまなシナリオでのテストが可能です。
playwright-ruby-client の導入手順
まず Capybara が Playwright の API を使用できる様にするための Driver を導入する必要があります。
capybara-plawright-driver という Gem を playwright-ruby-client の作者が公開しているのですが、以下の理由から利用せず Driver を自作しました。
- Capybara の API を介して Playwright の API を使用すると仕様の関係上、Playwright の API を直接使用したのと比較して不正確さが高い(Flaky になりやすい)
- Playwright の便利な API が一部使用できない
- selenium-webdriver を単純に capybara-plawright-driver 置き換えて使用できるわけではなく、多くのテストコードに修正が発生する
Playwright Driver
自作した Driver は以下のようになります。一部の selenium-webdriver でつかえる Util 関数を使用できるようにするために #page
, #visit
等を Drive に定義し @playwright_page.define_singleton_method
を使用して playwright-ruby-client に関数を追加し、追加した関数内で Drive に定義した関数を実行しています。
(自作した Driver は公式に載っている手順に従って作成しています。)
# CI 上で実行されているかどうか
on_ci = ENV.has_key?('CI')
# Docker 上で実行されているかどうか
on_docker = File.exist?('/.dockerenv')
class PlaywrightNullDriver < Capybara::Driver::Base
def needs_server? = true
def wait? = true
def page=(val)
@page = val
end
def page
@page
end
def visit(path)
path
end
def current_url
page&.url
end
def html
page&.content
end
end
Capybara.register_driver(:playwright) { PlaywrightNullDriver.new }
RSpec.configure do |config|
# ...省略
config.around(:each, type: :system) do |example|
driven_by :rack_test
Capybara.current_driver = :playwright
# Rails server is launched here, at the first time of accessing Capybara.current_session.server
base_url = Capybara.current_session.server.base_url
# When running in CI or Docker, we run the browser in headless mode
headless = on_ci || on_docker
executor = if on_ci || on_docker
playwright_host = ENV.fetch('PLAYWRIGHT_HOST', 'localhost')
playwright_port = ENV.fetch('PLAYWRIGHT_PORT', '8080')
playwright_server = "ws://#{playwright_host}:#{playwright_port}/ws?browser=chromium"
Playwright.connect_to_playwright_server(playwright_server)
else
playwright_cli_executable_path = Rails.root.join('./node_modules/.bin/playwright')
Playwright.create(playwright_cli_executable_path:)
end
executor.playwright.chromium.launch(headless:, slowMo: 100) do |browser|
browser.new_context(baseURL: base_url, ignoreHTTPSErrors: true) do |context|
@playwright_browser = browser
@playwright_page = context.new_page
Capybara.current_session.driver.page = @playwright_page
context.enable_debug_console! unless headless
# Override the save_screenshot method that is used by capybara
@playwright_page.define_singleton_method(:save_screenshot) do |path|
dir = File.dirname(path)
FileUtils.mkdir_p(dir)
context.pages.last.screenshot(path: path, fullPage: true)
end
# goto メソッドをオーバーライドし、Capybara の visit メソッドと同じように動作するようにする
@playwright_page.define_singleton_method(:goto) do |path, timeout: nil, waitUntil: nil, referer: nil|
url = Capybara.current_session.visit(path)
main_frame.goto(url, timeout: timeout, waitUntil: waitUntil, referer: referer)
end
def browser
@playwright_browser
end
def page
@playwright_page
end
example.run
end
end
executor.stop
end
end
mesoddonotikann
selenim-drive のメソッドを playwright-ruby-client のメソッドへの置き換え作業は、ほぼほぼ正規表現を使用した置換で対応することができます。
playwright-ruby-driver の API の理解につながるので、この部分はぜひ selenium-webdrive のメソッドと playwright-ruby-driver のメソッドを比較して対応してみてください。
さらなる高速化
playwright の導入に加えて以下を使用することでさらにテストの高速化を目指すことができます。
- System Spec の
transactional_fixture
機能を ON に、データベースのトランザクションを使用したデータの初期化を行える様にする。 -
test-prof の
let_it_be
を使用し、データの作成回数を必要最低限にする。
導入した感想
Capybara + Selenium と比較して Capybara + Playwright は実際に以下の点で優れていました。
- Flaky なテストが大きく減らせた
- 実行時間もコンスタントにへる
- Selenium Driver には要素の表示を待つ関数がないため
wait
を使用して一定の時間必ず待つというコードがあったが、これを削除できるので実行時間の削減にダイレクトにつながる - Playwright 自体に GUI のデバッグツールが組み込まれているため、テストの検証・作成・修正のコストが大きく減らせる
課題
現状、ブラウザに表示されている日本語を探させようとすると稀にエラーが発生してしまう問題が発生しており解決できていません。
エラーログを見るかぎる文字コードが原因の様に見えるのですが、playwright-client-ruby のコードを読んでも、デバッグして文字コードを確認してみても UTF-8 となっており問題ないため原因がわかっていません。
このエラーが発生すると JUnit 形式のテスト結果ファイル出力にも失敗するようになってしますうのでどうにかして解決したいです。
Discussion