Rspec View Specs and Devise Example

Created: 2020-03-01, Last Updated: 2023-02-04

I have a few views that use current_user when rendering. I was unable how to get these tests how to work properly until last night. Here is the secret sauce that worked for me

First add this to spec/rails_helpers.rb

config.include Devise::Test::ControllerHelpers, type: :view

Then your test should look something like this

require 'rails_helper'

RSpec.describe "things/index", type: :view do
  before do
    sign_in create(:user) #This logs us in as non-admin
    assign(:things, [
      create(:thing, name: 'Foo'),
      create(:thing, name: 'Bar')
    ])
  end

  it "renders a list of things" do
    render
    expect(rendered).to match /Foo/
    expect(rendered).to match /Bar/
  end

  it "doesn't show link to edit or destroy the things" do
    render
    expect(rendered).not_to match /Edit/
    expect(rendered).not_to match /Destroy/
  end

  context "as an admin" do
    before do
      sign_in create(:user, :admin)
    end
    it "shows edit and delete" do
      render
      expect(rendered).to match /Edit/
      expect(rendered).to match /Destroy/
    end
  end
end

The important bit is the sign_in create(:user) or sign_in create(:user, :admin). In my app these are created from a factorybot factory with an admin trait that looks like this.

factory :user do
  email { Faker::Internet.email }
  password { "password"}
  password_confirmation { "password" }
  user_type { 'free' }

  trait :admin do
    user_type { 'admin' }
  end
end

Just for completeness this is what the index view looks like

<h1 class="subtitle is-1">Things</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @things.each do |thing| %>
      <tr>
        <td><%= link_to thing.name, thing %></td>
        <% if current_user.user_type == 'admin' || meal.user == current_user%>
          <td><%= link_to 'Edit', edit_thing_path(thing) %></td>
        <% else %>
          <td></td>
        <% end %>
        <% if current_user.user_type == 'admin' || thing.user == current_user%>
          <td><%= link_to 'Destroy', thing, method: :delete, data: { confirm: 'Are you sure?' } %></td>
        <% else %>
          <td></td>
        <% end %>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Thing', new_thing_path %>
Back