Pappeteerを用いたカピバラなしのレールアプリケーションのためのシステム試験


Refer this article instead if you can read Japanese sentences.
日本語を読める方はこちらの記事を見てください



背景
Railsは「システムテスト」を導入しました.これはカピバラの設定を容易にします.我々はしばしば“受け入れテスト”のためだけではなく、Rails/Vueや他のリッチJavascriptの機能を備えたRailsアプリケーションのUIテストのために使用することがあります.
カピバラDSLは、実際にはJavaScriptの豊富な機能を持つページで動作するための少し不正確です.
カピバラは、間隔10 msecで内部的にポーリングして、利用可能な要素と操作可能なものを待ちます.
https://github.com/teamcapybara/capybara/blob/0468de5a810aae75ab9de20447e246c5c35473f0/lib/capybara/node/base.rb#L91
私は、これが本当に主要な原因であるということを知りません、しかし、我々はしばしばカピバラを使っているフレーク/不安定なテストケースに直面するかもしれません.
一方、Puppeteer or Playwright 要素を待機させるための再利用可能なソリューションがあります.Page#waitForSelector and Page#waitForNavigation . (脚本家はより高度な機能を持っているauto-waiting )
私は実際にRailsアプリケーションでそれらを使用するためのライブラリを移植しました.
  • Rubyの脚本https://playwright-ruby-client.vercel.app/
  • ルビーのための人形https://github.com/YusukeIwaki/puppeteer-ruby
  • ただし、カピバラDSLからは使用できません.そこで、カピバラなしでシステムテストを構成し、Pitpeteerを使ってみましょう.

    カピバラがいない場合はどうなりますか?
    カピバラは巨大なフレームワークなので、カピバラなしで何が起こるかを想像するのは難しいでしょう.
    実際にカピバラなしのRailsアプリケーション機能を失う
  • 起動テストのHTTPサーバーの起動
  • カピバラDSL
  • 使用visit またはその他のメソッドが発生するNoMethodError の代わりに
  • システムスペック( on - rspec )あるいはsystemtestcase ( minitest )で、カピバラに依存します
  • しかしrspecレールはまだ機能を提供します
  • 取付type: :feature type: :system 例えば、メタデータ
  • 定義feature background scenario 機能仕様
  • だから実際に準備をしなければならない
  • 起動HTTPサーバー
  • ブラウザによる自動化の実行
  • これは人形師によって満たされる

  • カピバラなしでHTTPサーバーの起動テスト
    カピバラは本当に優しい
  • 利用可能なポート
  • ホストの設定
  • ウィンドブリックとプーマを起動させます
  • しかし、この瞬間にそれらを忘れましょう、そして、私たちがベースURLでpumaを起動しなければならないだけであると仮定してくださいhttp://127.0.0.1:3000カピバラの論理を参照するpreparing server and launching Puma server , 次に、以下のようにテストサーバーを起動するための最小限の設定を簡単に見つけることができます.
    RSpec.configure do |config|
      config.before(:suite) do
        # launching Rails server for system testing
        require 'rack/builder'
        testapp = Rack::Builder.app(Rails.application) do
          map '/__ping' do # debugging endpoint for heartbeat
            run ->(env) { [200, { 'Content-Type' => 'text/plain' }, ['OK']] }
          end
        end
    
        require 'rack/handler/puma'
        server_thread = Thread.new do
          Rack::Handler::Puma.run(testapp,
            Host: '127.0.0.1',
            Port: 3000,
            Threads: '0:4',
            workers: 0,
            daemonize: false,
          )
        end
    
        # Waiting for Rails server is ready, using Net::HTTP
        require 'net/http'
        require 'timeout'
        Timeout.timeout(3) do
          loop do
            puts Net::HTTP.get(URI("http://127.0.0.1:3000/__ping"))
            break
          rescue Errno::EADDRNOTAVAIL
            sleep 1
          rescue Errno::ECONNREFUSED
            sleep 0.1
          end
        end
      end
    
      # Configure puppeteer-ruby for automation
      config.around(type: :feature) do |example|
        Puppeteer.launch(channel: :chrome, headless: false) do |browser|
          @server_base_url = 'http://127.0.0.1:3000'
          @puppeteer_page = browser.new_page
          example.run
        end
      end
    end
    
    この設定をspec/support/integrationCount TestHandヘルパーに入れます.RBまたはあなたが好む別の場所は、今私たちは以下のような人形劇でシステムのテストを楽しむことができます!
    既に述べたように、システムスペック仕様のSpec Spec(RSpec Railsによって提供される)を使用することはできません.
    require 'rails_helper'
    
    describe 'example' do
      let(:base_url) { @server_base_url }
      let(:page) { @puppeteer_page }
    
      let(:user) { FactoryBot.create(:user) }
    
      it 'can browse' do
        page.goto("#{base_url}/tests/#{user.id}")
        page.wait_for_selector('input', visible: true)
        page.type_text('input', 'hoge')
        page.keyboard.press('Enter')
    
        text = page.eval_on_selector('#content', 'el => el.textContent')
        expect(text).to include('hoge')
        expect(text).to include(user.name)
      end
    end
    


    生産用の小さなリファクタリング
    前のセクションで紹介されたテストサーバーを起動するロジックは、本当に簡単です.しかし、ほとんどのユーザーは、それがあまりに汚いので、それをあなた自身の製品にコピー/ペーストすることを躊躇します
    いいえ心配は、ここでより多くのコードをより良いコードですRack::Server.start HTTPサーバを起動するにはthe implementation of rackup command
    class RackTestServer
      def initialize(app:, **options)
        @options = options
    
        @options[:Host] ||= '127.0.0.1'
        @options[:Port] ||= 3000
    
        require 'rack/builder'
        @options[:app] = Rack::Builder.app(app) do
          map '/__ping' do
            run ->(env) { [200, { 'Content-Type' => 'text/plain' }, ['OK']] }
          end
        end
      end
    
      def base_url
        "http://#{@options[:Host]}:#{@options[:Port]}"
      end
    
      def start
        require 'rack/server'
        Rack::Server.start(**@options)
      end
    
      def ready?
        require 'net/http'
        begin
          Net::HTTP.get(URI("#{base_url}/__ping"))
          true
        rescue Errno::EADDRNOTAVAIL
          false
        rescue Errno::ECONNREFUSED
          false
        end
      end
    
      def wait_for_ready(timeout: 3)
        require 'timeout'
        Timeout.timeout(3) do
          sleep 0.1 until ready?
        end
      end
    end
    
    このテストサーバでは、rspec設定ファイルは非常に短く簡単になります.
    RSpec.configure do |config|
      config.before(:suite) do
        # Launch Rails application
        test_server = RackTestServer.new(
          # options for Rack::Server
          # https://github.com/rack/rack/blob/2.2.3/lib/rack/server.rb#L173
          app: Rails.application,
          server: :puma,
          Host: '127.0.0.1',
          Port: 3000,
          daemonize: false,
    
          # options for Rack::Handler::Puma
          # https://github.com/puma/puma/blob/v5.4.0/lib/rack/handler/puma.rb#L84
          Threads: '0:4',
          workers: 0,
        )
    
        Thread.new { test_server.start }
        test_server.wait_for_ready
      end
    
      # Configure puppeteer-ruby for automation
      config.around(type: :feature) do |example|
        Puppeteer.launch(channel: :chrome, headless: false) do |browser|
          @server_base_url = 'http://127.0.0.1:3000'
          @puppeteer_page = browser.new_page
          example.run
        end
      end
    end
    
    さて、テストのためにhttpサーバを起動したい場合は、アプリケーションからカピバラを取り除くことができます.
    また、使用することができますrack-test_server あなた自身のracktestserverを定義する代わりに宝石.