Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: VS CodeのdevcontainerとGitHub ActionsでPlaywrightを使う

Justin Searlsさんの以下の記事を元に、PlaywrightをVS CodeのdevcontainerとGitHub Actionsで動かしました。セットアップは最小限にとどめています。

Rails: システムテストのドライバをSeleniumからPlaywrightに差し替えたら安定した(翻訳)

概要

要するに、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周りが賢いという評判です。

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/rails-new - GitHub

私の場合は以下のコマンドで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 testbin/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 sbin/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を削除して作り直す方がよいこともあります。

参考: 困ったときのヒント集 - Railsチュートリアル

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以下でした。

docker-composeを便利にするツール「dip」を使ってみた

まとめ

  • 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も、以下の記事で「システムテストは最小限にとどめておけ」と言っています(使うなとは言っていません)。

参考: System tests have failed

ところでDHHの記事タイトルは「システムテストが失敗した」「システムテストを(Railsに)導入したのは失敗だった」というダブルミーニングなのが面白いですね。

関連記事

Rails: 開発中のライブリロード機能をrails_live_reload gemに替えました


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。