resource, devise_mapping, resource_name, resource_classがないとエラーが出る。
request spec(Controllerからviewが呼ばれている)だと問題ない(Gemにある)けど、view spec(viewを直接render)では別途、用意してあげる必要があったのでメモしておきます。

RSpecで出たエラー

     Failure/Error: <%= form_for(resource, as: resource_name, url: create_user_session_path, html: { method: :post, novalidate: true }, local: true) do |form| %>
     
     ActionView::Template::Error:
       undefined local variable or method `resource' for #<ActionView::Base:0x0000000000d7a0>

     ActionView::Template::Error:
       undefined local variable or method `resource_name' for #<ActionView::Base:0x0000000000d980>
     Failure/Error: <% if devise_mapping.rememberable? %>
     
     ActionView::Template::Error:
       undefined local variable or method `devise_mapping' for #<ActionView::Base:0x0000000000d9a8>
     Failure/Error: <% if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
     
     ActionView::Template::Error:
       undefined local variable or method `resource_class' for #<ActionView::Base:0x0000000000d9d0>

view spec

spec/views/users/sessions/new.html.erb_spec.rb

require 'rails_helper'

RSpec.describe 'users/sessions/new', type: :view do
+  before { @resource = User.new }

  context do
    it '対象の送信先と項目が含まれる' do
      render
      assert_select 'form[action=?][method=?]', create_user_session_path, 'post' do
        assert_select 'input[name=?]', 'user[email]'
        assert_select 'input[name=?]', 'user[password]'
        assert_select 'input[name=?]', 'user[remember_me]'
        assert_select 'input[name=?]', 'commit'
      end
    end

    it '対象のパスが含まれる' do
      render
      expect(rendered).to include("href=\"#{new_user_registration_path}\"") # アカウント登録
      expect(rendered).to include("href=\"#{new_user_password_path}\"") # パスワード再設定
      expect(rendered).to include("href=\"#{new_user_confirmation_path}\"") # メールアドレス確認
      expect(rendered).to include("href=\"#{new_user_unlock_path}\"") # アカウントロック解除
    end
    it '対象のパスが含まれない' do
      render
      expect(rendered).not_to include("href=\"#{new_user_session_path}\"") # ログイン
    end
  end
end

@resourceをbeforeでセットして、下のhelperに渡す。
(ログインのパスはform actionと同じなので、href=も入れました)

app/helpers/devise_view_spec_helper.rb

module DeviseViewSpecHelper
  # Gets the actual resource stored in the instance variable
  def resource
    # instance_variable_get(:"@#{resource_name}")
    @resource
  end

  # Attempt to find the mapped route for devise based on request path
  def devise_mapping
    # @devise_mapping ||= request.env["devise.mapping"]
    Devise.mappings[resource.class.name.underscore.to_sym]
  end

  # Proxy to devise map name
  def resource_name
    devise_mapping.name
  end

  # Proxy to devise map class
  def resource_class
    devise_mapping.to
  end
end

helperを用意して、undefined local variable or methodを回避。
普通のhelperなので、通常のリクエストやrequest specからも呼ばれそうですが、GemのDeviseController(devise-4.7.3/app/controllers/devise_controller.rb)が呼ばれるので、結果的にview specでしか使われていない。

controller_nameがセットされない

     Failure/Error: expect(rendered).not_to include("href=\"#{new_user_session_path}\"") # ログイン
       expected "<div class=\"card card-body mb-2\" style=\"max-width: 480px\">\n  <h5 class=\"card-title mb-3\">ログイン...</li>\n    <li><a href=\"/users/unlock/new\">アカウントロック解除</a></li>\n  </ul>\n</nav>\n</div>\n</div>\n" not to include "href=\"/users/sign_in\""

app/views/users/shared/_links.html.erb

<% if controller_name != 'sessions' %>
    <li><%= link_to 'ログイン', new_user_session_path %></li>
<% end %>

Controller通ってない(view specな)ので、controller_nameがnilになりリンク非表示のテストが通らない。
ここではhelper使わずにテンプレに変数を渡して対応しました。(呼び出し元は明確なので)

app/views/users/sessions/new.html.erb

-  <div class="card-footer pb-0 px-0"><%= render 'users/shared/links' %></div>
+  <div class="card-footer pb-0 px-0"><%= render 'users/shared/links', action: 'sign_in' %></div>

app/views/users/shared/_links.html.erb

- <% if controller_name != 'sessions' %>
+ <% if action != 'sign_in' %>
    <li><%= link_to 'ログイン', new_user_session_path %></li>
<% end %>

actionとmethodの順番が逆

     Failure/Error:
       assert_select 'form[action=?][method=?]', update_user_password_path(reset_password_token: params[:reset_password_token]), 'post' do
         assert_select 'input[name=?]', 'user[password]'
         assert_select 'input[name=?]', 'user[password_confirmation]'
         assert_select 'input[name=?]', 'commit'
       end
     
     Nokogiri::CSS::SyntaxError:
       unexpected 'post' after '[:equal, "\"/users/password\""]'

他は、form[action=?][method=?](action、methodの順)でしたが、
ここだけ、form[method=?][action=?](method、actionの順)でした。
(form_forの定義は他と同じ感じですが)

spec/views/users/passwords/edit.html.erb_spec.rb

require 'rails_helper'

RSpec.describe 'users/passwords/edit', type: :view do
  before do
    @resource = User.new
    params[:reset_password_token] = SecureRandom.uuid
  end

  context do
    it '対象の送信先と項目が含まれる' do
      render
-      assert_select 'form[action=?][method=?]', update_user_password_path(reset_password_token: params[:reset_password_token]), 'post' do
+      assert_select 'form[method=?][action=?]', 'post', update_user_password_path(reset_password_token: params[:reset_password_token]) do
        assert_select 'input[name=?][value=?]', '_method', 'put'
        assert_select 'input[name=?]', 'user[password]'
        assert_select 'input[name=?]', 'user[password_confirmation]'
        assert_select 'input[name=?]', 'commit'
      end
    end

    it '対象のパスが含まれる' do
      render
      expect(rendered).to include("href=\"#{new_user_session_path}\"") # ログイン
      expect(rendered).to include("href=\"#{new_user_registration_path}\"") # アカウント登録
      expect(rendered).to include("href=\"#{new_user_confirmation_path}\"") # メールアドレス確認
      expect(rendered).to include("href=\"#{new_user_unlock_path}\"") # アカウントロック解除
    end
    it '対象のパスが含まれない' do
      render
      expect(rendered).not_to include("href=\"#{new_user_password_path}\"") # パスワード再設定
    end
  end
end

今回のコミット内容
※他のページも同様の修正を入れています。
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/44cd7d5bed2debfa449102014b2c454ac4b42d9c


序でにPUT/PATCH/DELETEをPOSTに変更して、GETとPOSTに統一しました。
(フロント実装時に気にする要素が減るので良さそう)
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/5902e7c47248e223d0f890fcb92cdd472fd3a27f

フロント(Nuxt)も修正
https://dev.azure.com/nightonly/nuxt-app-origin/_git/nuxt-app-origin/commit/01a2d7d518a5f17e8c00c110fa2bdffbcb9f899f

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です