Open29

RSpec関連📝䞀郚GitHub Actions含む

なかじなかじ

開発環境

カテゎリヌ 䜿甚技術
フロント゚ンド Rails 7.1.3.4, TailwindCSS, DaisyUI Javascript
バック゚ンド Rails 7.1.3.4 (ruby 3.2.3)
むンフラ heroku / AmazonS3
DB MySQL
開発環境 Docker

資料

factory bot関連

https://qiita.com/piggydev/items/32717b6c382272e2134e

GitHub Actionsの導入の流れはこちら

参考資料

なかじなかじ

ファむル䜜成

RSpec蚭定ファむル䜜成

$ docker compose exec web bundle exec rails g rspec:install

モデルスペック䜜成

$ docker compose exec web bundle exec rails generate rspec:model User #モデル名は単数頭文字倧文字

factory botのファむル䜜成

$ docker compose exec web bundle exec rails g factory_bot:model user 
  • なぜかこれを実行するずtest/factories/*.rbに䜜成される
  • ただ䞋蚘のファむルが自動でロヌドされるらしいので問題なさそう
factories.rb
test/factories.rb
spec/factories.rb
factories/*.rb
test/factories/*.rb
spec/factories/*.rb
なかじなかじ

spec/rails_helper.rbでの蚭定忘れた

1) User 名前、メヌルがあり、パスワヌドは3文字以䞊であれば有効であるこず
     Failure/Error: user = build(:user)
     
     NoMethodError:
       undefined method `build' for #<RSpec::ExampleGroups::User "名前、メヌルがあり、パスワヌドは3文字以䞊であれば有効であるこず" (./spec/models/user_spec.rb:4)>
     # ./spec/models/user_spec.rb:5:in `block (2 levels) in <top (required)>'
  • buildっおなんですか〜ず蚀われる

蚭定の远加

#spec/rails_helper.rb
config.include FactoryBot::Syntax::Methods
  • できた🙆‍♀
なかじなかじ

ガバレッゞ衚瀺のgemの芋方

https://zenn.dev/soramarjr/articles/2b542c7bd7c71d

  • VScodeのLive Serverを䜿甚

https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer

  • ファむルはcoverage/index.htmlでGo LiveするVScode右䞋
  • このファむルは.gitignoreに远加しおありたす
なかじなかじ

Userのenum任意項目

  • 決たった倀で入力できるかのテスト
  it '䞖代ず性別が入力できるこず' do
    user = build(:user)
    user.age = (0..5).to_a.sample
    user.gender = (0..1).to_a.sample
    expect(user).to be_valid
  end
  • 蚭定しおいる芁玠をランダムで取り出す圢にしおみた
なかじなかじ

RSpecの導入

資料

その他資料

DBをテスト環境で察応

      - name: Database create and migrate
        run: |
          cp config/database.ci.yml config/database.yml
          bundle exec rake db:create
          bundle exec rake db:migrate
rake aborted!
ActiveRecord::AdapterNotSpecified: The `development` database is not configured for the `development` environment. (ActiveRecord::AdapterNotSpecified)

  Available database configurations are:
  • development環境の蚭定が䞍足しおいるこずが原因

テスト環境のDBに蚭定しおみる

- name: Database create and migrate
  run: |
    cp config/database.ci.yml config/database.yml
    RAILS_ENV=test bundle exec rake db:create
    RAILS_ENV=test bundle exec rake db:migrate

config/database.ci.ymlのlocalhostの蚭定を127.0.0.1:3306にしたChatGPT・NG

Please check your database configuration and ensure there is a valid connection to your database.
Couldn't create 'rails7-mysql' database. Please check your configuration.
rake aborted!
ActiveRecord::DatabaseConnectionError: There is an issue connecting with your hostname: 127.0.0.1. (ActiveRecord::DatabaseConnectionError)
#...
Caused by:
Mysql2::Error::ConnectionError: Can't connect to MySQL server on '127.0.0.1:3306' (111) (Mysql2::Error::ConnectionError)
/home/runner/work/metime-meals/metime-meals/vendor/bundle/ruby/3.2.0/gems/mysql2-0.5.6/lib/mysql2/client.rb:97:in `connect'
/home/runner/work/metime-meals/metime-meals/vendor/bundle/ruby/3.2.0/gems/mysql2-0.5.6/lib/mysql2/client.rb:97:in `initialize'
なかじなかじ

rubocop,RSpecをdeployのneedsに

倱敗䟋

  deploy:
    if: github.ref == 'refs/heads/main'
    needs: rubocop, rspec
  • Invalid workflow file: .github/workflows/deploy.yml#L64
    The workflow is not valid. .github/workflows/deploy.yml (Line: 64, Col: 12): Job 'deploy' depends on unknown job 'rubocop, rspec'.

成功䟋

  deploy:
    if: github.ref == 'refs/heads/main'
    needs: 
      - rubocop
      - rspec
  • 配列圢匏にしおありたす
なかじなかじ

databasemyapp_testの名前合わせるずりあえず今日はここたで

#config/database.ci.yml
test:
  adapter: mysql2
  charset: utf8mb4
  collation: utf8mb4_bin
  encoding: utf8mb4
  database: myapp_test
  host: 127.0.0.1
  username: root
  password: password
  reconnect: true
#.github/workflows/deploy.yml
name: rubocop, rspec, deploy

on:
  push:
    branches:
  
jobs:
  rspec:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    services:
      mysql:
        image: mysql:8.0
        ports:
          - 3307:3306
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_ROOT_HOST: '%'
        options: --health-cmd "mysqladmin ping -h 127.0.0.1" --health-interval 20s --health-timeout 10s --health-retries 10

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Bundler and gem install
        run: |
          gem install bundler
          bundle install --jobs 4 --retry 3 --path vendor/bundle

      - name: Wait for MySQL
        run: sleep 15

      - name: Database create and migrate
        run: |
          cp config/database.ci.yml config/database.yml
          bundle exec rails db:create RAILS_ENV=test
          bundle exec rails db:migrate RAILS_ENV=test
  
      - name: Run rspec
        run: bundle exec rspec

  rubocop:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Run rubocop
        run: bundle exec rubocop -a

  deploy:
    if: github.ref == 'refs/heads/main'
    needs: 
      - rubocop
      - rspec
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: akhileshns/heroku-deploy@v3.13.15
        with:
          heroku_api_key: ${{secrets.HEROKU_API_KEY}}
          heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
          heroku_email: ${{ secrets.HEROKU_EMAIL }}
なかじなかじ

presence: trueではないけどnull:falseであるものに぀いお

コヌド

#test/factories/bookmarks.rb
FactoryBot.define do
  factory :bookmark do
    association :user
    association :post
  end
end
#spec/models/bookmark_spec.rb
  context 'ナヌザヌず掲瀺板の組み合わせがナニヌクでない堎合' do
    it '無効であるこず' do
      bookmark = create(:bookmark)
      new_bookmark = build(:bookmark, user: bookmark.user, post: bookmark.post)
      new_bookmark.valid?
      expect(new_bookmark.errors[:user_id]).to include('はすでに存圚したす'), 'bookmarkずuserのナニヌクバリデヌションが蚭定されおいたせん'
    end
  end

゚ラヌ文

  1) Bookmark ナヌザヌず掲瀺板の組み合わせがナニヌクでない堎合 無効であるこず
     Failure/Error: bookmark = create(:bookmark)
     
     ActiveRecord::NotNullViolation:
       Mysql2::Error: Column 'latitude' cannot be null
     # /usr/local/bundle/gems/mysql2-0.5.6/lib/mysql2/client.rb:151:in `_query'
     # /usr/local/bundle/gems/mysql2-0.5.6/lib/mysql2/client.rb:151:in `block in query'
     # /usr/local/bundle/gems/mysql2-0.5.6/lib/mysql2/client.rb:150:in `handle_interrupt'
     # /usr/local/bundle/gems/mysql2-0.5.6/lib/mysql2/client.rb:150:in `query'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/evaluation.rb:15:in `create'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/strategy/create.rb:12:in `block in result'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/strategy/create.rb:9:in `result'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/factory.rb:45:in `run'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/factory_runner.rb:29:in `block in run'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/factory_runner.rb:28:in `run'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/strategy/create.rb:5:in `association'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/evaluator.rb:33:in `association'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/attribute/association.rb:19:in `block in to_proc'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/evaluator.rb:75:in `instance_exec'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/evaluator.rb:75:in `block in define_attribute'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/attribute_assigner.rb:59:in `get'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/attribute_assigner.rb:16:in `block (2 levels) in object'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/attribute_assigner.rb:15:in `each'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/attribute_assigner.rb:15:in `block in object'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/attribute_assigner.rb:14:in `object'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/evaluation.rb:10:in `object'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/strategy/create.rb:9:in `result'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/factory.rb:45:in `run'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/factory_runner.rb:29:in `block in run'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/factory_runner.rb:28:in `run'
     # /usr/local/bundle/gems/factory_bot-6.5.0/lib/factory_bot/strategy_syntax_method_registrar.rb:28:in `block in define_singular_strategy_method'
     # ./spec/models/bookmark_spec.rb:13:in `block (3 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # Mysql2::Error:
     #   Column 'latitude' cannot be null
     #   /usr/local/bundle/gems/mysql2-0.5.6/lib/mysql2/client.rb:151:in `_query'

解決策

  • Postファクトリヌにlongitudeずlatitudeを远加した
#test/factories/posts.rb
FactoryBot.define do
  factory :post do
    sequence(:restaurant_name) { |n| "タむトル#{n}" }
    sequence(:body) { |n| "本文#{n}" }
    sequence(:address) { |n| "䜏所#{n}" }
    genre { [0, 1, 2, 3, 4, 10, 11, 20, 21, 99].sample }
    rating { [0, 1, 2, 3, 4].sample }
    latitude { 35.6895 }
    longitude { 139.6917 }
    association :user
  end
end
なかじなかじ

describe/context/it

describe

  • テストの察象を説明
    • モデルの堎合はクラス名やメ゜ッド名
    • システムスペックの堎合は機胜名「ナヌザヌからの泚文」などが倚い

context

  • 状況や条件を説明
    • 「〜〜の堎合」など
    • contextを䜿う堎合、状況や条件を指定するコヌドが必芁なこずが倚い

it

  • itでは期埅する動䜜・結果を説明する
    • 「〜〜であるこず」や「〜〜できるこず」などで終わる衚珟になるこずが倚い
    • 「正しいこず」のように䜕が正しいかわからない感じで曞かない
    • 具䜓的に「〇〇なら〇〇になる」ずいった感じで曞く
    • 準備はletやbeforeの䞭に曞くこずで、準備・実行・怜蚌は分離させた䞊、距離は近く
  • 匕甚元コヌドレビュヌで孊ぶ Ruby on Rails 第二版SG Rails
なかじなかじ

テストコヌドの泚意点

  • テストの説明は具䜓的に曞く
  • 期埅倀に䜿う倀は同じテストコヌド内で準備する(pp.70-72)
    • FactoryBotで䜜る際は期埅倀を指定
  • ナヌザヌに衚瀺される画面を想定したテストを曞く(pp.72-73)
    • 登録された内容が詳现画面にアクセスした際衚瀺できるかを確認
  • 遅延評䟡を䜿う堎合beforeを䜿う
    • let/let!を䜿甚するこずでスコヌプが広くなり可読性を䞋げる
    • it~doずいう曞き方も良い
    • むンスタンス倉数を䜿う
  • 入力倀や期埅倀はベタ曞きをする
  • 頻出する定矩はtraitを䜿う
  • 「されないこず」だけのテストは䜜成しない。同時にできる堎合のテストも実装する。
  • テストデヌタの呜名はわかりやすく
  • 䞍芁な倀=怜蚌に䞍芁な倀はテストに含めない
  • 匕甚元コヌドレビュヌで孊ぶ Ruby on Rails 第二版SG Rails
なかじなかじ

システムスペック開発環境Docker

Gemの導入

# Gemfile
group :test do
  gem 'capybara'
  gem 'selenium-webdriver'
  gem "webdrivers"
end

webdriverの蚭定

  • テストを実行する環境の蚭定
# spec/rails_helper.rb
#...
# ファむルの読み蟌み蚭定
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
#...

RSpec.configure do |config|
#...
  config.before(:each, type: :system) do
    driven_by :remote_chrome
    Capybara.server_host = IPSocket.getaddress(Socket.gethostname)
    Capybara.server_port = 4444
    Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}"
    Capybara.ignore_hidden_elements = false
  end
#...
end

capybaraの蚭定

# spec/support/capybara.rb
Capybara.register_driver :remote_chrome do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument('no-sandbox')
  options.add_argument('headless')
  options.add_argument('disable-gpu')
  options.add_argument('window-size=1680,1050')
  Capybara::Selenium::Driver.new(app, browser: :remote, url: ENV['SELENIUM_DRIVER_URL'], capabilities: options)
end

䞊蚘の蚭定のみだず䞋蚘゚ラヌ

 1) ナヌザヌ登録 正しいタむトルが衚瀺されおいるこず
     Got 0 failures and 2 other errors:

     1.1) Failure/Error: visit '/users/new'
          
          Errno::ECONNREFUSED:
            Failed to open TCP connection to 127.0.0.1:4444 (Connection refused - connect(2) for "127.0.0.1" port 4444)
          # /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1603:in `initialize'
          # /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1603:in `open'
          # /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1603:in `block in connect'
          # /usr/local/bundle/gems/timeout-0.4.1/lib/timeout.rb:186:in `block in timeout'
          # /usr/local/bundle/gems/timeout-0.4.1/lib/timeout.rb:193:in `timeout'
          # /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1601:in `connect'
          # /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1580:in `do_start'
          # /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1575:in `start'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/http/default.rb:65:in `start'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/http/default.rb:59:in `http'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/selenium/patches/persistent_client.rb:14:in `http'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/http/default.rb:118:in `response_for'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/http/default.rb:75:in `request'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/http/common.rb:67:in `call'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/bridge.rb:635:in `execute'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/bridge.rb:76:in `create_session'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/common/driver.rb:323:in `block in create_bridge'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/common/driver.rb:322:in `create_bridge'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/common/driver.rb:73:in `initialize'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/remote/driver.rb:38:in `initialize'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/common/driver.rb:57:in `new'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver/common/driver.rb:57:in `for'
          # /usr/local/bundle/gems/selenium-webdriver-4.22.0/lib/selenium/webdriver.rb:89:in `for'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:75:in `browser'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:95:in `visit'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/session.rb:281:in `visit'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in `call'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in `visit'
          # ./spec/system/users_spec.rb:3:in `block (2 levels) in <top (required)>'
          # ------------------
          # --- Caused by: ---
          # Errno::ECONNREFUSED:
          #   Connection refused - connect(2) for "127.0.0.1" port 4444
          #   /usr/local/bundle/gems/net-http-0.4.1/lib/net/http.rb:1603:in `initialize'

compose.ymlに䞋蚘远蚘

# compose.yml
    environment:
      TZ: Asia/Tokyo
      # システムスペックで远蚘
      SELENIUM_DRIVER_URL: http://chrome:4444/wd/hub
      # ここたで
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy
      # システムスペックで远蚘
  chrome:
    image: seleniarm/standalone-chromium:latest
    ports:
      - 4444:4444
    # ここたで
  • コンテナ立ち䞊げ盎すず解消
  • localhost:4444を開くずSelenium Gridのペヌゞが開きたす

参考

なかじなかじ

クラッシュの察凊法

  • ブラりザのサむズを小さくする
 1) Users ログむン埌 ナヌザヌ線集 フォヌムの入力倀が正垞 ナヌザヌの線集が成功する
     Got 0 failures and 4 other errors:

     1.1) Failure/Error: visit root_path
          
          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-headless-shell=121.0.6167.85)
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/response.rb:63:in `add_cause'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/response.rb:41:in `error'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/response.rb:52:in `assert_ok'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/response.rb:34:in `initialize'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/http/common.rb:101:in `new'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/http/common.rb:101:in `create_response'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/http/default.rb:103:in `request'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/http/common.rb:67:in `call'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/bridge.rb:685:in `execute'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/remote/bridge.rb:119:in `get'
          # /usr/local/bundle/gems/selenium-webdriver-4.25.0/lib/selenium/webdriver/common/navigation.rb:32:in `to'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/selenium/driver.rb:95:in `visit'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/session.rb:281:in `visit'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in `call'
          # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in `visit'
          # ./spec/support/login_macros.rb:3:in `login_as'
          # ./spec/system/users_spec.rb:61:in `block (3 levels) in <top (required)>'
          # ------------------
          # --- Caused by: ---
          # Selenium::WebDriver::Error::WebDriverError:
          #   #0 0xaaaab0cf08e8 <unknown>

Capybaraの蚭定を远加

Capybara.register_driver :remote_chrome do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument('no-sandbox')
  options.add_argument('headless')
  options.add_argument('disable-gpu')
  options.add_argument('window-size=1024,768')
  options.add_argument('disable-dev-shm-usage') # /dev/shmの䜿甚を無効にしおメモリを盎接䜿甚
  options.add_argument('disable-software-rasterizer') # ゜フトりェアラスタラむザヌを無効にする
  options.add_argument('disable-extensions') # 拡匵機胜を無効にする
  Capybara::Selenium::Driver.new(app, browser: :remote, url: ENV['SELENIUM_DRIVER_URL'], capabilities: options)
end

参考

https://qiita.com/oieioi/items/0e9468c1d2ad2da1a94c

https://qiita.com/kawagoe6884/items/cea239681bdcffe31828

https://qiita.com/mitopanda/items/3ca5a9a26ceb7e2cf034

なかじなかじ

1ペヌゞに2぀の同じ芁玠がある堎合

  1) Users ログむン埌 ナヌザヌ線集 フォヌムの入力倀が正垞 ナヌザヌの線集が成功する
     Failure/Error: click_link 'ログむン'
     
     Capybara::Ambiguous:
       Ambiguous match, found 2 elements matching link "ログむン"
     
     [Screenshot Image]: /myapp/tmp/capybara/failures_r_spec_example_groups_users_nested_2_nested_nested_-_17.png

察応策

https://qiita.com/nao0725/items/8b2afeee4ab5943c6400

なかじなかじ

発生する案件

  • 同じテストを通しおも成功する時ず倱敗する時がある
  • 「ログむンしおください」の画面がスクリヌンショットで衚瀺されおいる

成功

$ docker compose exec web bundle exec rspec spec/system/users_spec.rb
DEPRECATION WARNING: #fog_provider is deprecated and has no effect (called from block in <top (required)> at /myapp/config/initializers/carrierwave.rb:7)
DEPRECATION WARNING: Calling warn on ActiveSupport::Deprecation is deprecated and will be removed from Rails (use your own Deprecation object instead) (called from block in <top (required)> at /myapp/config/initializers/carrierwave.rb:7)

Users
  ログむン前
    ナヌザヌの新芏登録
      フォヌムの入力倀が正垞
2024-10-09 15:30:00 INFO Selenium [:logger_info] Details on how to use and modify Selenium logger:
  https://selenium.dev/documentation/webdriver/troubleshooting/logging

2024-10-09 15:30:00 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:00 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの新芏䜜成が成功する
      メヌルアドレスが未入力
2024-10-09 15:30:00 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:00 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの新芏䜜成が倱敗する
      登録枈みのメヌルアドレスを䜿甚
2024-10-09 15:30:01 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:01 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの新芏䜜成が倱敗する
    マむペヌゞ
      ログむンしおいない状態
2024-10-09 15:30:01 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:01 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        マむペヌゞのアクセスが倱敗する
  ログむン埌
    ナヌザヌ線集
      フォヌムの入力倀が正垞
WARNING: ignoring the provided expectation message argument({:wait=>5}) since it is not a string or a proc. Called from /myapp/spec/system/users_spec.rb:71:in `block (5 levels) in <top (required)>'.
2024-10-09 15:30:02 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:02 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの線集が成功する

Finished in 3.23 seconds (files took 1.76 seconds to load)
5 examples, 0 failures

Coverage report generated for RSpec to /myapp/coverage.
Line Coverage: 23.91% (121 / 506)

倱敗

$ docker compose exec web bundle exec rspec spec/system/users_spec.rb
DEPRECATION WARNING: #fog_provider is deprecated and has no effect (called from block in <top (required)> at /myapp/config/initializers/carrierwave.rb:7)
DEPRECATION WARNING: Calling warn on ActiveSupport::Deprecation is deprecated and will be removed from Rails (use your own Deprecation object instead) (called from block in <top (required)> at /myapp/config/initializers/carrierwave.rb:7)

Users
  ログむン前
    ナヌザヌの新芏登録
      フォヌムの入力倀が正垞
2024-10-09 15:30:46 INFO Selenium [:logger_info] Details on how to use and modify Selenium logger:
  https://selenium.dev/documentation/webdriver/troubleshooting/logging

2024-10-09 15:30:46 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:46 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの新芏䜜成が成功する
      メヌルアドレスが未入力
2024-10-09 15:30:46 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:46 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの新芏䜜成が倱敗する
      登録枈みのメヌルアドレスを䜿甚
2024-10-09 15:30:47 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:47 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの新芏䜜成が倱敗する
    マむペヌゞ
      ログむンしおいない状態
2024-10-09 15:30:47 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:47 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        マむペヌゞのアクセスが倱敗する
  ログむン埌
    ナヌザヌ線集
      フォヌムの入力倀が正垞
2024-10-09 15:30:50 WARN Selenium [:clear_session_storage] [DEPRECATION] clear_session_storage is deprecated and will be removed in a future release. 
2024-10-09 15:30:50 WARN Selenium [:clear_local_storage] [DEPRECATION] clear_local_storage is deprecated and will be removed in a future release. 
        ナヌザヌの線集が成功する (FAILED - 1)

Failures:

  1) Users ログむン埌 ナヌザヌ線集 フォヌムの入力倀が正垞 ナヌザヌの線集が成功する
     Failure/Error: fill_in '名前', with: '倪郎'
     
     Capybara::ElementNotFound:
       Unable to find field "名前" that is not disabled
     
     [Screenshot Image]: /myapp/tmp/capybara/failures_r_spec_example_groups_users_nested_2_nested_nested_-_349.png

     
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/node/finders.rb:312:in `block in synced_resolve'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/node/base.rb:84:in `synchronize'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/node/finders.rb:301:in `synced_resolve'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/node/finders.rb:60:in `find'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/node/actions.rb:91:in `fill_in'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/session.rb:774:in `fill_in'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in `call'
     # /usr/local/bundle/gems/capybara-3.40.0/lib/capybara/dsl.rb:52:in `fill_in'
     # ./spec/system/users_spec.rb:67:in `block (5 levels) in <top (required)>'

Finished in 4.94 seconds (files took 1.79 seconds to load)
5 examples, 1 failure

Failed examples:

rspec ./spec/system/users_spec.rb:65 # Users ログむン埌 ナヌザヌ線集 フォヌムの入力倀が正垞 ナヌザヌの線集が成功する

Coverage report generated for RSpec to /myapp/coverage.
Line Coverage: 22.13% (112 / 506)
Stopped processing SimpleCov as a previous error not related to SimpleCov has been detected

ログむンモゞュヌルにリダむレクト远加

なかじなかじ

コン゜ヌルでパスを確認

userずother_userを蚭定

[1] pry(main)> user = User.first
  User Load (1.3ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User:0x00
 id: 1,
 email: "koko0@email",
 crypted_password: "xxxxxxxxx",
 salt: "xxxxxx",
 name: "倪郎0",
 created_at: Thu, 18 Jul 2024 17:12:56.589333000 JST +09:00,
 updated_at: Thu, 18 Jul 2024 17:12:56.589333000 JST +09:00,
 reset_password_token: nil,
 reset_password_token_expires_at: nil,
 reset_password_email_sent_at: nil,
 access_count_to_reset_password_page: 0,
 avatar: nil,
 gender: nil,
 age: nil>
[2] pry(main)> other_user = User.last
  User Load (3.6ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
=> #<User:0x00
 id: 29,
 email: "rant@pom",
 crypted_password: "xxxxxxxxx",
 salt: "xxxxxx",
 name: "koko",
 created_at: Sun, 15 Sep 2024 13:49:41.149525000 JST +09:00,
 updated_at: Sun, 15 Sep 2024 13:49:52.392101000 JST +09:00,
 reset_password_token: nil,
 reset_password_token_expires_at: nil,
 reset_password_email_sent_at: nil,
 access_count_to_reset_password_page: 0,
 avatar: nil,
 gender: nil,
 age: "teens">

パスの確認

[9] pry(main)> app.edit_mypage_profiles_path(user)
=> "/mypage/profiles/edit.1"
[10] pry(main)> app.edit_mypage_profiles_path(other_user)
=> "/mypage/profiles/edit.29"

参考

https://qiita.com/morioka1206/items/c65267bfea1a13920059

なかじなかじ

アむコンをタップするシステムスペック

  • お気に入り機胜はアむコンをタップするこずによっお登録される

参考

お気に入りからのマむペヌゞ

やりたいこず

  • 投皿を䜜成
  • その投皿をお気に入り
  • 自分のお気に入り䞀芧で衚瀺

曞いたコヌド

      context '投皿をお気に入り' do
        it 'お気に入りした投皿が衚瀺される' do
          create(:post, restaurant_name: 'レストランお気に', body: 'ずおもおいしいな', user: user )
          visit post_path(post)
          link = find('#bookmark-button-for-post-#{post.id}') #䜜成した投皿のお気に入りボタンを指定したかった
          link.click
          visit mypage_bookmark_posts_path
          expect(page).to have_content('レストランお気に')
          expect(page).to have_content('ずおもおいしいな')
        end
      end

倱敗①

Failures:

  1) Users ログむン埌 マむペヌゞ 投皿をお気に入り お気に入りした投皿が衚瀺される
     Failure/Error: visit post_path(post)
     
     ArgumentError:
       wrong number of arguments (given 0, expected 1)
     
     [Screenshot Image]: /myapp/tmp/capybara/failures_r_spec_example_groups_users_nested_2_nested_2_nested_2_-_342.png

     
     # ./spec/system/users_spec.rb:102:in `block (5 levels) in <top (required)>'

Finished in 6 seconds (files took 1.84 seconds to load)
  • postの匕数がないですよ〜〜
  • post = create(:post, restaurant_name: 'レストランお気に', body: 'ずおもおいしいな', user: user )で解決

倱敗

  1) Users ログむン埌 マむペヌゞ 投皿をお気に入り お気に入りした投皿が衚瀺される
     Failure/Error: link = find('#bookmark-button-for-post-#{post.id}')
     
     Selenium::WebDriver::Error::InvalidSelectorError:
       invalid selector: An invalid or illegal selector was specified
         (Session info: chrome-headless-shell=121.0.6167.85); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#invalid-selector-exception
  • link = find("#bookmark-button-for-post-#{post.id}")匏展開するならダブルクォヌおションでしょうずChatGPTに半笑いで指摘された被害劄想

䞀旊完成

      context '投皿をお気に入り' do
        it 'お気に入りした投皿が衚瀺される' do
          post = create(:post, restaurant_name: 'レストランお気に', body: 'ずおもおいしいな', user: user )
          visit post_path(post)
          link = find("#bookmark-button-for-post-#{post.id}")
          link.click
          visit current_path
          visit mypage_bookmark_posts_path
          expect(page).to have_content('レストランお気に')
          expect(page).to have_content('ずおもおいしいな')
        end
      end
  • Turbo系のずころがリロヌドしないずうたくいかないのなんだろうな〜
なかじなかじ

ログアりトテスト

  • ログアりトの時、 data-confirm出おきおOKしなきゃログアりトできない
      it 'ログアりト凊理が成功する' do
        click_link 'ログアりト', match: :first # ログアりトっお名前のリンク2぀あるので片方に指定
        # ログアりトが成功したこずを確認するテスト
        page.accept_alert 'ログアりトしおもよろしいですか'
        expect(page).to have_content 'ログむン'
      end
    end

accept_alertっおなんだろう

なかじなかじ

䜿えるRSpec入門・その1「RSpecの基本的な構文や䟿利な機胜を理解する」 #Ruby - Qiita

  • 1぀のexampleに耇数の゚クスペクテヌションを眮くずどこでテストに萜ちおいるのか分からないのでやめた方が良い
  • describe はいく぀でも曞けるし、ネストさせるこずもできる
  • 䞀番倖偎のdescribe以倖はRSpecを省略できるよ
  • before do ... end で囲たれた郚分は example の実行前に毎回呌ばれる=共通の準備があるずきはそれで曞くず分かりやすいブロックなのでbefore { login_as(user) }ず曞いた郚分も同じですな
  • letのメリットは遅延評䟡関連蚘事参考
  • subjectずいうものもあっおたずめお曞けるがあんたり䜿わない僕がRSpecでsubjectを䜿わない理由 - give IT a try
  • 技巧的なテストコヌドは避けたしょう。テストコヌドはDRYさよりも読みやすさを重芖したしょう。
  • contextやitのあずは日本人しか読たないなら日本語で良い
  • it/example/specifyぱむリアス
  • shared_examplesずit_behaves_likeずいう機胜を䜿うず、example を再利甚するこずができる
  • shared_contextずinclude_contextを䜿うず、 context を再利甚するこずができる
  • letの遅延評䟡が原因でテストに萜ちる際は、let!を䜿甚する
  • pending実行を続けお保留する=知らん間にテスト通るようになっおた珟象が消える
  • skip問答無甚でテストを止める
  • xitexample䞞ごずスキップ
  • xdescribe/xcontextグルヌプ䞞ごずスキップ

関連蚘事RSpecのletを䜿うのはどんなずきか翻蚳 #Ruby - Qiita

  • letはメ゜ッドを䜜るのでtypoするずNameErrorが発生する
  • 無駄な初期化の時間を枛らせる
  • ロヌカル倉数をそのたたletに眮き換えられる
  • 単玔に読みやすい
  • 遅延初期化されるのでパスするはずのテストがパスしない案件が発生する
なかじなかじ

䜿えるRSpec入門・その2「䜿甚頻床の高いマッチャを䜿いこなす」 #Ruby - Qiita

  • マッチャnot🍵期埅倀ず実際の倀を比范しお、䞀臎したもしくは䞀臎しなかったずいう結果を返すオブゞェクト。expect(...).to xxx のtoの盎埌に出おくるや぀がマッチャなのでto自䜓はマッチャではないです
  • not_toずto_notどっちでもいいけどずりあえずドキュメンをでよく出おくるnot_toを䜿うのはいかがでしょう
  • expect(A).to be BA.equal?(B)、すなわち同䞀むンスタンスであればパスする
  • expect(A).to eq BA == B、すなわち同倀であればパスするこっち䜿っおいるな。eq login_pathをbeに眮き換えたらテスト萜ちるんだろうか
  • empty? のようにメ゜ッド名が「?」で戻り倀がbooleanになるメ゜ッドをbe_emptyのような圢で怜蚌できる
  • RSpecで真停倀を確認するテストを曞く堎合は特別な事情がない限り、be_truthy/`be_falseず曞くのがおすすめ
  • changeは䜕かの倀が倉わる際の蚘述ができる。特にリレヌション関係で蚘述したい際に䟿利参考
  • include配列に含たれおいるこず
  • raise_error゚ラヌが起こるこず
  • be_within + of揺らぎがある際の振れ幅を蚭定できる
なかじなかじ

to beずto eqの違いを詊しおみる

コヌド

### 実隓
```ruby
  describe 'ログむン埌' do
    context 'ログアりトボタンをクリック' do
      it 'ログアりト凊理が成功する' do
        login_as(user)
        click_link 'ログアりト', match: :first
        # ログアりトが成功したこずを確認するテスト
        page.accept_alert 'ログアりトしおもよろしいですか'
        expect(page).to have_content 'ログむン'
        visit current_path
        expect(current_path).to be root_path #eqからbeに倉曎。eqの時はテストパスしおたした
      end
    end
  end

実行結果

      ログアりト凊理が成功する (FAILED - 1)

Failures:

  1) UserSessions ログむン埌 ログアりトボタンをクリック ログアりト凊理が成功する
     Failure/Error: expect(current_path).to be root_path
     
       expected #<String:32860> => "/"
            got #<String:32880> => "/"
     
       Compared using equal?, which compares object identity,
       but expected and actual are not the same object. Use
       `expect(actual).to eq(expected)` if you don't care about
       object identity in this example.
     
     [Screenshot Image]: /myapp/tmp/capybara/failures_r_spec_example_groups_user_sessions_nested_2_nested_-_822.png

     
     # ./spec/system/user_sessions_spec.rb:39:in `block (4 levels) in <top (required)>'
  • むンスタンスが違いたすよ〜っお倱敗した面癜い
  • ちゃんず曞き換えの提案たでしおくださるのね...
なかじなかじ

GitHub Actionsのスピヌドアップ

  • 気になる👀
  • キャッシュの䜿甚はやっおみたい
  • 導入圓初ありえんくらい倱敗続きでキャッシュの削陀しおた気がする〜〜

https://zenn.dev/starfish/books/6966f2e8450a70/viewer/209d24

なかじなかじ

create_listに぀いお

  • FactoryBotで耇数のデヌタ䜜りたいなっお時に䜿う

https://qiita.com/kodai_0122/items/e755a128f1dade3f53c6

  • 定矩堎所探そうず思ったけどどうやらメタプロ䜿っおいるらしいので断念

https://zenn.dev/hamchance/scraps/2063a0796159b9

なかじなかじ

PBT

  • 入力倀のプロパティヌを指定するこずによっおテストが曞けるらしい
  • 入力倀に具䜓的な倀ではなく、敎数のような性質を指定しお100回テストしおくれる

https://github.com/ohbarye/pbt

https://tech.findy.co.jp/entry/2024/05/20/164206

なかじなかじ

䜿えるRSpec入門・その4「どんなブラりザ操䜜も自由自圚逆匕きCapybara倧蟞兞」 #Ruby - Qiita

  • 必芁に応じお読み返したしょう蚘事
  • HTML䞊でa芁玠であればボタンではなくリンクになるのでclick_link 'New User'になる
  • ボタンかリンクか分からない堎合は、click_on 'New User'が䟿利
  • 画像のalt属性click_linkかclick_on
  • ファむルを添付するattach_file
  • hiddenに倀をセットするfindを䜿うず画面䞊にある芁玠しか怜玢しないので、visible: falseオプションを぀ける。地図の怜玢の時に䜿えそう。
  • ラベルがないフィヌルドも操䜜可胜=>ラベルあるはずなのに指定できないなぁっお時はHTMLを確認したしょう
  • 特定のタグやCSS芁玠に特定の文字列が衚瀺されおいるか怜蚌重芁なお知らせが衚瀺されおるか確認できるのかしら
  • ペヌゞ内に特定のリンク・ボタンがあるか、チェックボックスにチェックがされおいるかのように、珟状の怜蚌を行う堎合もあるよ
  • レスポンスのステヌタスコヌドを怜蚌するAPIカリキュラムで出おきおいたなexpect(response).to have_http_status(:ok)

文字列で指定できない堎合

  • classを指定'.settings-link'
  • idで指定'#settings-link'

耇数の芁玠の絞り蟌み

  • withinでスコヌプを絞る
within '.section-drug' do
  choose 'はい'
end

within '.section-disease' do
  choose 'いいえ'
end
  • これ倧事だな。クラスでたずめおないずテスト曞けないのな。
  • 「テストしにくいviewはテストをしやすいように倉曎する」ずある。確かに。

その他

  • save_and_open_pageでテスト倱敗した堎合の画面が開く
  • JSのテストjs: trueにする。「JavaScriptを実行する堎合は、PoltergeistのようなJavaScriptドラむバの蚭定が必芁になりたす。」ずあるので蚭定が必芁かも
  • JSのテストが原因䞍明で萜ちるこずがあるrspec-retryずいうgemを䜿うずいいらしい