1
votes

I'm having an issue logging in a user with Devise/Rspec to hit a route for testing. I'm used the support/controller_macros module as outlined by devise, but whenever I try using login_user in any part of my test I get the error: before is not available from within an example (e.g. an it block) or from constructs that run in the scope of an example (e.g. before, let, etc). It is only available on an example group (e.g. a describe or context block).

I've tried a moving things around, making sure all of my requires are set up correctly, etc.

My test:

require "rails_helper"

RSpec.describe BallotsController, type: :controller do
  describe "index" do
    it "renders" do
      login_user
      ballots_path
      expect(response).to be_success
      expect(response).to render_template("index")
    end
  end
end

(I've tried adding login_user inside the describe block, and the upper block as well)

My controller_macros:

  def login_user
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user_confirmed]
      user = FactoryBot.create(:user_confirmed)
      sign_in user
    end
  end

  def login_admin
    before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:admin]
      user = FactoryBot.create(:admin)
      sign_in user
    end
  end
end

My spec helper:

require "rails_helper"
require_relative "support/controller_macros"
RSpec.configure do |config|
  # rspec-expectations config goes here. You can use an alternate
  # assertion/expectation library such as wrong or the stdlib/minitest
  # assertions if you prefer.
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups
  config.include ControllerMacros, :type => :controller
  config.include Devise::Test::ControllerHelpers, :type => :controller

  config.example_status_persistence_file_path = "spec/examples.txt"

  config.disable_monkey_patching!

  if config.files_to_run.one?
    config.default_formatter = "doc"
  end

  config.profile_examples = 10

  config.order = :random

  Kernel.srand config.seed
end

I expect it to log a user in, and for the controller to correctly hit the index route. Thank you!

1

1 Answers

2
votes

Only issue in the code(to resolve the error, without digging into the debate of if this method should be used or not) is that you have login user within the it block, which can't be because it is calling before(:each).

If you see the device documentation, you will also see that it does not have this call in it block, but rather outside of it block in a describe block. Which applies this call to all it blocks in that describe block.

Your code willcbecome:

RSpec.describe BallotsController, type: :controller do
  describe "index" do
    login_user
    it "renders" do
      ballots_path
      expect(response).to be_success
      expect(response).to render_template("index")
    end
  end
end

The way I prefer:

In your controller_macros, replace the login_user with:

def login_user(user)
  @request.env["devise.mapping"] = Devise.mappings[:user_confirmed]
  sign_in user
end

Now, wherever you want to login user, you can do it something like:

RSpec.describe BallotsController, type: :controller do
  describe "index" do
    let(:user) { FactoryBot.create(:user) }

    it "renders" do
      login_user(user)
      ballots_path
      expect(response).to be_success
      expect(response).to render_template("index")
    end
  end

  # OR

  describe "index" do
    let(:user) { FactoryBot.create(:user) }

    before(:each) do
      login_user(user)
    end

    it "renders" do
      ballots_path
      expect(response).to be_success
      expect(response).to render_template("index")
    end
  end
end