「Rails + Vue」 System spec が実行されなかった際の対処法

問題

selenium_chrome_headless」を使用してsystem specを実行した際、通るはずの簡単なテストが通りませんでした。しかも、失敗した際のスクショが真っ白という謎の現象に襲われました。

  • 検証する画面

Image from Gyazo

# ユーザー登録画面で必須項目が未入力により登録に失敗するというテストです。
require 'rails_helper'

RSpec.describe "ユーザー登録", type: :system, js: true do
  context 'ユーザー情報に不備がある場合' do
    before do
      visit '/register'
      binding.pry
      click_on 'メールアドレスで登録'
    end
    it '新規登録に失敗する' do
      expect(page).to have_content 'ニックネームは必須項目です'
    end
  end
end
  • 「メールアドレスで登録」というボタンが見つからないとエラーが吐かれる!!
    しかも、下記のように真っ白...

Image from Gyazo

原因の究明

① capybaraが実際に動いているか?

application.html.erbでvue.jsを読み込む設定をしていたので、下記のようにbody要素内に適当な文字を設定して表示されるか確認しました。

# views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Arrangy</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= javascript_pack_tag 'hello_vue' %>
  </head>

  <body>
    <%= yield %>
    <p>hoge</p>  # <= 表示されるか確認!!
  </body>
</html>

結果は表示された為、capybaraは問題なく実行されているようです。

② webpackerでコンパイルされているのか?

こちらも「public/packs-test」下にコンパイルされたファイルを確認できたので違いました。

Image from Gyazo

③ headlessを使用せずに、ブラウザ上で確認してみる。

Image from Gyazo

めっちゃエラー吐いてる...

// app/frontend/plugins/axios.js

import axios from 'axios';

let csrf_token = document.getElementsByName('csrf-token')[0].content; #<= ここでエラーが発生している!!
const axiosInstance = axios.create({
  baseURL: '/api',
  headers: { 'X-CSRF-TOKEN': csrf_token },
});

export default axiosInstance;

axiosを使用してリクエストを送る際、csrf-tokenをheaderに埋め込む設定をしていました。 しかし、test環境では「csrf-token」が生成されていないにも関わらず、値を取得しに行っていた為にエラーが吐かれていました。

しかし、エラーが発生しているコードがないと開発、本番環境ではRails側から怒られてしまいます...

解決策

メドピアさんの記事を参考にしました。
jsのライブラリとして提供されている「rails-ujs」があるみたいですのでそちらを使用します。

ライブラリ追加後、ファイルを以下のように修正。

import axios from 'axios';
import { csrfToken } from 'rails-ujs';  // <= ここ!!

const axiosInstance = axios.create({
  baseURL: '/api',
  headers: { 'X-CSRF-TOKEN': csrfToken() },
});

export default axiosInstance;

これで無事に解決できました!!