Docker+circleCI+ajaxでのいいね機能のRspec/systemテスト


 この記事で解決すること

ajax(js)を使ったいいね機能のRspec/systemテストの方法

参考記事

Rails + Selenium + DockerでSystemSpecの環境構築
Railsでいいね機能を実装。Ajaxを使い非同期対応。で
RSpec+Capybaraでajaxとかcssとかを待つ
rspec-retryでfeature specを安定させる
Capybara::ElementNotFound 回避に wait させるか retry させるか
使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」
[RSpec] CapybaraでCSSの●番目の要素を指定したい!(同じ要素が複数あるとき)

 開発環境

macOS Catalina 10.51.1
Docker version 19.03.4, build 9013bf5
ruby 2.6.0
Rails 5.2.3
RSpec 3.9
capybara 3.29.0
selenium-webdriver 3.142.6

docker環境でのsystemスペック

下記の記事を参考に構築
この設定をしないとjsを利用したテストでエラーが発生しました

Rails + Selenium + DockerでSystemSpecの環境構築

テストするコード

下記の記事を参考にコードを書きました。
js.erbを使っているのがポイントです

Railsでいいね機能を実装。Ajaxを使い非同期対応。で

Ajax(js)を使用しているときのポイント

selenium_chrome_headlessを使用する

→docker環境でのsystemスペックの記事を参考にするとこれは解決

jsを使っているとajaxの処理完了まで時間かかるので対策をする

・wait timeを設定する
・テストを繰り返す

wait timeを設定する

この設定をすることでajaxの処理完了を待つ
設定は下記の記事を参考にしてください

RSpec+Capybaraでajaxとかcssとかを待つ

2.テストを繰り返す

rspec-retryを利用して非同期処理が終わるまでリトライしてもらいます
設定は下記の記事を参考にしてください

rspec-retryでfeature specを安定させる

テストコード

likes_spec.rb
require 'rails_helper'

# js: trueがないとリクエストフォーマットエラーが発生する
# retry:5で失敗したときに何回まで繰り返すかを設定
RSpec.describe "Likes", type: :system, js: true, retry: 5  do 
  let(:user) { create(:user)}
  let(:other_user) { create(:user)}

  describe 'create' do
    scenario 'ログイン済みユーザ/他人の投稿にはいいねできる' do
      post = create(:post, user: user)
      sign_in other_user
      visit root_path
      #今回いいねのリンクでfontawosameを使用していてテキストを指定できなかったのでhave_linkをhave_cssで代用
      expect(page).to have_css('.likes-link-create') 
      expect(page).not_to have_css('.likes-link-delete')
      expect {
        # 同じクラスを持った要素が複数存在したので1つ目を指定。fontawosameを利用しているのでリンクをクラス名で指定
        first('.likes-link-create').click
        # 上記で設定したwait timeのメソッドを使用してajaxの処理完了を待つ
        wait_for_ajax
      }.to change{ post.likes.count }.by(1)
    end
  end
...省略
end

 CircleCIを使用しているときの注意点

ローカル環境よりもajaxの処理完了時間がかかる
→ローカルであればwait time・テストを繰り返すのどちらかを設定すればテストは成功していたのですが、circleciでのRspecテストでは片方だけだと失敗してしまうので今回は両方を設定しています
wait timeを伸ばしたり繰り返す回数を増やしましたが解決しなかったので両方を設定
selenium_chrome_headlessを使う上で新しい設定を.circleci/config.ymlには追加してません

最後に

今回の記事を書くためにいろんな方の記事を参考にさせていただきました。ありがとうございました!
この記事が同じような課題を直面している人の助けになれば嬉しいです
そしてよりよい方法を知っている方がいればコメントなどで教えていただけると助かります!!