Justin Searlsさんの以下の記事を元に、PlaywrightをVS CodeのdevcontainerとGitHub Actionsで動かしました。セットアップは最小限にとどめています。
概要
要するに、PlaywrightをVS Code + devcontainerのシステムテストで使うというものです。
今回は、rails new
と--devcontainer
オプションで生成されるdevcontainer環境をできるだけ活かした形でセットアップしました。
tree .devcontainer/
.devcontainer/
├── Dockerfile
├── compose.yaml
└── devcontainer.json
.devcontainer/ディレクトリの下のファイルのうち、Dockerfileには手を加えませんでした。
compose.yamlからはSeleniumを削除するだけです。devcontainer.jsonを更新して、Seleniumのセットアップを削除し、Playwrightのセットアップを追加しました。
その他は、test/application_system_test_case.rbのセットアップをSeleniumからPlaywrightに更新したのと、GitHub Actions用に.github/workflows/ci.ymlをPlaywright用に更新しました。
.github/
├── dependabot.yml
└── workflows
└── ci.yml
最後に、devcontainerをGitHub Codespaces上でも動かしてみました。
環境
- Rails 7.2〜Rails 8
- Ruby: 3.3.6
- VS Code: バージョン: 1.95.1
- Docker: 27.3.1
Playwrightについて
Playwrightは、Microsoftが公開しているNode.jsアプリで、ChromeやFirefoxやSafariを使ってWebアプリをテストできます。特にwait周りが賢いという評判です。
そういう事だった。
> Auto-wait. Playwright waits for elements to be actionable prior to performing actions.https://t.co/rQjVRwtVx2
— 中谷 一郎 | ichiroc (@ichiroc) November 1, 2024
Rails 7.1からplaywright-ruby-clientがサポートされているのが強みです(ウォッチ20230906)。
以下の発表でもPlaywrightを導入して成果を上げています。
Playwrightは、以下の記事によるとPuppeteerと同じ開発チームが手掛けているそうです。
参考: Playwrightも知らないで開発してる君たちへ #JavaScript - Qiita
Webアプリのテストで昔から使われているWebDriver(Selenium)は実績はあるものの、セットアップが大変で、テストがflakyになりがちとされています。
Playwrightは(Puppeteerも)npmでインストールするので、Playwrightをdevcontainer内で使うには、何らかの方法でdevcontainer内でnpmが動くようにする必要があります。
rails new
で生成されるdevcontainerをベースにする
Playwrightで使うブラウザは、さしあたってChromeだけにしておきました。
普通にrails new
してもよいのですが、せっかくなので公式に加わったrails-new
を使うことにします。インストール方法は以下のリポジトリのREADMEをお読みください。
私の場合は以下のコマンドでRailsアプリとdevcontainerセットアップを生成しました。
rails-new -u 3.3.5 -r 8.0.0 enno_jp --devcontainer -a propshaft -c tailwind --database=postgresql --skip-action-mailer --skip-action-cable --skip-action-mailbox --skip-action-text --skip-jbuilder
-u
オプションでRubyバージョンを、-r
オプションでRailsバージョンを指定し、その後ろにアプリ名を指定します。
そこから後ろには、通常のrails new
と同じオプションを渡せます。そのオプションに--devcontainer
を指定することで、devcontainer用のファイルも生成されるようになります。
このディレクトリをVS Codeで開くとdevcontainerが動き出そうとしますが、セットアップが終わる前に「コンテナーで再度開く」をクリックすると、devcontainer更新後にビルドをやり直すはめになって余計な時間がかかります(大した違いはありませんが)。
私の場合、セットアップが終わるまではdevcontainerを動かさないように、つまり「コンテナーで再度開く」をクリックするのを避けて、別のエディタで作業しました。
1. Seleniumの設定をdevcontainerから削除する
# .devcontainer/compose.yaml
...
depends_on:
- - selenium
- postgres
- selenium:
- image: selenium/standalone-chromium
- restart: unless-stopped
-
...
2. Playwrightの設定を追加する
Playwrightセットアップ用のスクリプトを、最近のRailsで公式になったscriptディレクトリに置き、chmod 755 script/setup
で実行権限を追加します。
# script/setup
#!/usr/bin/bash
export PLAYWRIGHT_CLI_VERSION=$(bundle exec ruby -e 'require "playwright"; puts Playwright::COMPATIBLE_PLAYWRIGHT_VERSION.strip')
yarn add -D "playwright@$PLAYWRIGHT_CLI_VERSION"
yarn run playwright install
yarn playwright install-deps
devcontainer.jsonにNodeを導入して、その中でPlaywrightをインストールします。本当はcompose.yamlでPlaywrightを導入したかったのですが、こちらは今後やってみます。
postCreateCommand
に上のscript/setup
も追加します。
# devcontainer/devcontainer.json
...
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
- "ghcr.io/rails/devcontainer/features/postgres-client": {}
+ "ghcr.io/rails/devcontainer/features/postgres-client": {},
+ "ghcr.io/devcontainers/features/node:1": {}
},
"containerEnv": {
"CAPYBARA_SERVER_PORT": "45678",
- "SELENIUM_HOST": "selenium",
+ "PLAYWRIGHT_BROWSER": "chromium",
+ "PLAYWRIGHT_HEADLESS": "true",
"DB_HOST": "postgres"
},
...
- "postCreateCommand": "bin/setup --skip-server"
+ "postCreateCommand": "bin/setup --skip-server && script/setup"
selenium-webdriver gemをcapybara-playwright-driverに差し替えます。ついでに、Macで不要なtzinfo-data gemも削除しておきます。
# Gemfile
...
- gem "tzinfo-data", platforms: %i[ windows jruby ]
...
group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara"
- gem "selenium-webdriver"
+ gem "capybara-playwright-driver" # MyConfig
end
ここまで終わったら、プロジェクトディレクトリをVS Codeで開き、「devcontainerで開き直しますか?」と聞かれたら「コンテナーで再度開く」をクリックすると、コンテナのビルドが始まります。
前述したように、Playwrightはnodeアプリなので、ここで行っているインストール方法だとアプリにnode_modules/
が作成されます。Playwrightがダウンロードしたブラウザもここに保存されます。
せっかくimportmap-railsなどを使ってnodeなしの生活を送れるかと思っていたので、ここだけが個人的に残念でした(差し障りは何もありませんが)。Playwright設定済みのNodeイメージをcompose.yamlの方で導入すれば、node_modules/
が作成されなくなるかもしれないので、今後調べてみます。
3. システムテストの設定を更新する
コンテナのビルドが無事終わってディレクトリを普通に操作できるようになったら、次はシステムテストの設定を更新します。viewport
でスクショのサイズも指定しています。
# test/application_system_test_case.rb
require "test_helper"
-class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
- if ENV["CAPYBARA_SERVER_PORT"]
- served_by host: "rails-app", port: ENV["CAPYBARA_SERVER_PORT"]
+Capybara.register_driver :my_playwright do |app|
+ Capybara::Playwright::Driver.new(app,
+ browser_type: +ENV["PLAYWRIGHT_BROWSER"]&.to_sym || :chromium,
+ headless: (false unless ENV["CI"] || ENV["PLAYWRIGHT_HEADLESS"])),
+ viewport: { width: 900, height: 1800 })
+end
- driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ], options: {
- browser: :remote,
- url: "http://#{ENV["SELENIUM_HOST"]}:4444"
- }
- else
- driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ]
- end
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :my_playwright
end
ここまでできたら、VS Codeのターミナルでbin/rails test
とbin/rails test:system
を実行し、エラーにならないことを確認しましょう。
$ bin/rails test:system
Rebuilding...
Done in 297ms.
Running 4 tests in a single process (parallelization threshold is 50)
Run options: --seed 4584
# Running:
Capybara starting Puma...
* Version 6.4.3, codename: The Eagle of Durango
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:35429
....
Finished in 3.281278s, 1.2190 runs/s, 1.2190 assertions/s.
4 runs, 4 assertions, 0 failures, 0 errors, 0 skips
4. GitHub ActionsにPlaywrightをセットアップする
ローカルのdevcontainerでPlaywrightが動くようになったので、次はRailsが生成するGitHub ActionでもシステムテストがPlaywrightで動くようにします。
▶ci.ymlの変更(クリックで展開)
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches: [ main ]
jobs:
scan_ruby:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Scan for common Rails security vulnerabilities using static analysis
run: bin/brakeman --no-pager
scan_js:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Scan for security vulnerabilities in JavaScript dependencies
run: bin/importmap audit
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Lint code for consistent style
run: bin/rubocop -f github
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3
# redis:
# image: redis
# ports:
# - 6379:6379
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Install packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 postgresql-client
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
+ - name: Use Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.x'
+
+ - name: Install dependencies
+ run: yarn --frozen-lockfile; yarn run playwright install
+
+ - name: Cache Playwright Chromium browser
+ id: playwright-cache
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/ms-playwright
+ key: playwright-browsers-${{ runner.os }}-${{ hashFiles('yarn.lock') }}
+
+ - name: Install Playwright Chromium browser (with deps)
+ if: steps.playwright-cache.outputs.cache-hit != 'true'
+ run: yarn run playwright install --with-deps chromium
+
+ - name: Install Playwright Chromium browser deps
+ if: steps.playwright-cache.outputs.cache-hit == 'true'
+ run: yarn run playwright install-deps chromium
+
- name: Run tests
env:
RAILS_ENV: test
DATABASE_URL: postgres://postgres:postgres@localhost:5432
# REDIS_URL: redis://localhost:6379/0
run: bin/rails db:test:prepare test test:system
- name: Keep screenshots from failed system tests
uses: actions/upload-artifact@v4
if: failure()
with:
name: screenshots
path: ${{ github.workspace }}/tmp/screenshots
if-no-files-found: ignore
あと、M* Mac(Apple Silicon)環境の場合は、GitHub Actionsを動かすためにおそらく以下を実行してGemfile.lockの更新もコミットしておく必要もあります。
bundle lock --add-platform x86_64-linux
終わったら、各自GitHubにリポジトリを作成してpushしてみましょう。無事CIが動けば完了です!
参考: GitHub CodespacesでPlaywrightのシステムテストを動かしてみる
ここまでできたので、もしかするとGitHub CodespacesでPlaywrightのシステムテストが動くのでは?と思ってやってみました。
GitHubのリポジトリで、以下の「Create codespace on main」ボタンをクリックすればビルドが始まります。
1分ほどしてビルドが終わったので、コンソールで普通のテスト(bin/rails test
)とシステムテスト(bin/rails test:system
)を実行すれば、動くはずです。🎉
トラブルシューティング
システムテスト(bin/rails test:system
)が以下のようにコケている場合は、単にPlaywrightのインストール(script/setup
)が完了していないことを意味しています。
その場合は、GitHub Codespacesに以下のスクリプトを貼り付けて再実行することでPlaywrightをインストールすれば、システムテストが動くようになるはずです。
export PLAYWRIGHT_CLI_VERSION=$(bundle exec ruby -e 'require "playwright"; puts Playwright::COMPATIBLE_PLAYWRIGHT_VERSION.strip')
yarn add -D "playwright@$PLAYWRIGHT_CLI_VERSION"
yarn run playwright install
yarn playwright install-deps
今度はうまくいきました!
一度作成したGitHub Codespaceは、2度目からはスムーズに開くようになります。
GitHub CodespacesでRailsを動かすときのtips
GitHub Codespacesでbin/rails s
やbin/dev
を実行すると、そのままでは以下のようなエラーが発生します。
ここに表示されている長ったらしいホスト名をconfig.hosts << "fuzzy-space-journey-w59gqvqr5g2x7g-3000.app.github.dev"
のようにconfig/environments/development.rbファイルに追加すれば一応修正できます。
それよりも単にconfig.hosts.clear
をconfig/environments/development.rbファイルに追加することでホスト名による制約を無効にしておく方がスマートです。
以下の記事には、この他にもさまざまなGitHub Codespacesのトラブルシューティングが記載されています。GitHub Codespacesのトラブルシューティングがうまくいかない場合、Codespaceを削除して作り直す方がよいこともあります。
devcontainerのサイズは大きい
devcontainerのイメージサイズ(以下の第1項目)は2.79GBと表示されており、割と大きいことに気づきました(なお、第2項目はdevcontainerをビルドするときにだけ使われるイメージなので、ビルドが終われば削除しても構いません)。
生成される.devcontainer/Dockerfileは以下のようにGitHubのコンテナリポジトリから取得するようになっています。
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=3.3.6
FROM ghcr.io/rails/devcontainer/images/ruby:$RUBY_VERSION
イメージにはライブラリが一通り入っていないと不便なはずなので、フレームワークが用意するイメージのサイズが大きくなるのは仕方なさそうです。
とはいうものの、devcontainerはproduction用ではないので、PlaywrightとChromeも含めてこのサイズならよしとしておこうと思います。
参考までに以下は、従来使っていた開発用のイメージで、dip
コマンドで使うことが前提でした。こちらはスリム化を図っていたこともあり、イメージのサイズは1GB以下でした。
まとめ
- devcontainerを使うと、Dockerコンテナ内でエディタを動かすのと同等の形で開発できるようになる
--devcontainer
オプションを指定してrails new
すると、従来よりもさらに手軽にRailsアプリを生成してdevcontainerを即動かせるようになる- rails-newアプリを使えば、Dockerだけ用意しておけば環境を汚さずにRailsアプリを生成できる
- devcontainerでPlaywrightを使えるようにセットアップすると、GitHub CodespacesでもPlaywrightによるシステムテストが動く
- Playwrightを動かすには、何らかの形でnpmが動く環境が必要
- (importmap-rails環境ならnpmなしにしたかったので、今後何とかしたい)
devcontainerでSeleniumの代わりにPlaywrightを使うオプションが公式でサポートされればよいのにと思っています。できればnode_modules/
ディレクトリを作らない方法で。
おまけ: Seleniumの不安定さを改めて実感
リリース直後のRails 8.0.0で、試しにSeleniumとPlaywrightの違いを軽く試してみました。
システムテストを生成させるためだけに、適当なscaffoldをいくつか実行してみました。
$ bin/rails g scaffold Article title:string body:text
$ bin/rails g scaffold Post title:string body:text
その結果、Seleniumでは、生成したシステムテストが2つまでなら普通に動作したのですが、システムテストを3つに増やしただけで、途端に謎のエラーが発生しました。正直、Seleniumってこんなに不安定なの?という気持ちです。
$ bin/rails g scaffold Comment title:string body:text
▶エラー(クリックで展開)
$ bin/rails test:system
Running 12 tests in a single process (parallelization threshold is 50)
Run options: --seed 38589
# Running:
Capybara starting Puma...
* Version 6.4.3, codename: The Eagle of Durango
* Min threads: 0, max threads: 4
* Listening on http://172.21.0.3:45678
...........E
Error:
ArticlesTest#test_should_destroy_Article:
Selenium::WebDriver::Error::UnknownError: unknown error: session deleted because of page crash
from unknown error: cannot determine loading status
from tab crashed
(Session info: chrome=130.0.6723.69)
test/system/articles_test.rb:38:in `block in <class:ArticlesTest>'
Error:
ArticlesTest#test_should_destroy_Article:
Selenium::WebDriver::Error::InvalidSessionIdError: invalid session id
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:63:in `add_cause'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:41:in `error'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:52:in `assert_ok'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/common.rb:101:in `new'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/common.rb:101:in `create_response'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/default.rb:103:in `request'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/common.rb:67:in `call'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/bridge.rb:685:in `execute'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/bridge.rb:292:in `screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/driver.rb:333:in `screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/takes_screenshot.rb:58:in `screenshot_as'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/takes_screenshot.rb:60:in `screenshot_as'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/takes_screenshot.rb:39:in `block in save_screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/takes_screenshot.rb:39:in `open'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/takes_screenshot.rb:39:in `save_screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:152:in `save_screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara/session.rb:748:in `block in save_screenshot'
<internal:kernel>:90:in `tap'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara/session.rb:748:in `save_screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/actionpack-8.0.0/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb:116:in `save_image'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/actionpack-8.0.0/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb:46:in `take_screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/actionpack-8.0.0/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb:56:in `take_failed_screenshot'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/actionpack-8.0.0/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb:10:in `before_teardown'
Error:
ArticlesTest#test_should_destroy_Article:
Selenium::WebDriver::Error::InvalidSessionIdError: invalid session id
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:63:in `add_cause'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:41:in `error'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:52:in `assert_ok'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/common.rb:101:in `new'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/common.rb:101:in `create_response'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/default.rb:103:in `request'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/http/common.rb:67:in `call'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/bridge.rb:685:in `execute'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/remote/bridge.rb:231:in `window_handles'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/selenium-webdriver-4.26.0/lib/selenium/webdriver/common/driver.rb:210:in `window_handles'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:244:in `window_handles'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara/selenium/driver_specializations/chrome_driver.rb:40:in `reset!'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara/session.rb:132:in `reset!'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara.rb:327:in `block in reset_sessions!'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara.rb:327:in `reverse_each'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/capybara-3.40.0/lib/capybara.rb:327:in `reset_sessions!'
/home/vscode/.rbenv/versions/3.3.6/lib/ruby/gems/3.3.0/gems/actionpack-8.0.0/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb:16:in `after_teardown'
bin/rails test test/system/articles_test.rb:37
Finished in 3.763158s, 3.1888 runs/s, 2.9231 assertions/s.
12 runs, 11 assertions, 0 failures, 1 errors, 0 skips
一方Playwrightでは、システムテストをscaffoldで3〜7つ追加しても、少なくともこうしたエラーは発生しませんでした。
Playwrightの場合、scaffoldのシステムテストを1個追加するたびに、体感的に0.5秒ずつ線形に時間が増えていく感じでした。
やはりシステムテストは件数を増やすよりも、長めのシステムテストをせいぜい2〜3件ぐらい書くにとどめておくのがよさそうです。可能なら機能テストでカバーするか、ViewComponentを導入してビューのテストを増やすほうがよいのかもしれないと思いました。
DHHも、以下の記事で「システムテストは最小限にとどめておけ」と言っています(使うなとは言っていません)。
ところでDHHの記事タイトルは「システムテストが失敗した」「システムテストを(Railsに)導入したのは失敗だった」というダブルミーニングなのが面白いですね。