I'm writing a simple controller test to ensure that we get a 200 status upon a GET request. Every answer I've encountered online says to make sure we are including the correct Devise
helper in spec_helper.rb
, but nothing I've tried seems to have helped.
Error:
NoMethodError:
undefined method `sign_in' for #<RSpec::ExampleGroups::ApplicationFunnelController::Index::WhenAccessed "returns 200" (./spec/requests/application_funnel_controller_spec.rb:7)>
Controller test:
require 'spec_helper'
RSpec.describe ApplicationFunnelController, type: :request do
describe "index" do
context "when accessed" do
it "returns 200" do
user = FactoryBot.create(:user)
group = FactoryBot.create(:group, name: 'Testers')
user.groups << group
sign_in user
get become_a_member_path
expect(response.status).to eq 200
end
end
end
end
spec_helper.rb:
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
require "database_cleaner"
require "rspec/rails"
require "capybara/rspec"
require "capybara/rails"
require "devise"
class ActiveSupport::TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::TestHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :routing
config.include Devise::TestHelpers, type: :routing
config.infer_spec_type_from_file_location!
DEFAULT_CLEANER_STRATEGY = :transaction
def with_truncation
DatabaseCleaner.strategy = :truncation
yield
ensure
DatabaseCleaner.strategy = DEFAULT_CLEANER_STRATEGY
end
config.include ActiveSupport::Testing::TimeHelpers
config.before :suite do
ActiveJob::Base.queue_adapter = :test
DatabaseCleaner.strategy = DEFAULT_CLEANER_STRATEGY
unless ENV["SEED"] == "false"
DatabaseCleaner.clean_with(:truncation, except: %w(ar_internal_metadata schema_migrations))
end
Rails.application.load_tasks
end
config.before { ActionMailer::Base.deliveries.clear }
end
After a few search, and reading through the docs, I think a found an answer.
First of all, type: :request
in a controller spec will not work as far as I know. There is a difference between Controller Specs & Request Specs. I recommend you read the links provided so you can consider what type of test you want to include in your codebase.
Now, say you want to sign in a user factory in a Controller spec, what you can do is create a ControllerMacros
module, and place it in spec/support
:
# =================================
# spec/support/controller_macros.rb
module ControllerMacros
def login_user(user)
@request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
end
# ====================
# spec/rails_helper.rb
require_relative 'support/controller_macros'
RSpec.configure do |config|
# For Devise > 4.1.1
config.include Devise::Test::ControllerHelpers, :type => :controller
# Use the following instead if you are on Devise <= 4.1.1
# config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
# ======================================================
# spec/controllers/application_funnel_controller_spec.rb
RSpec.describe ApplicationFunnelController do
describe "index" do
context "when accessed" do
# You can optionally add this in you example.
before do
FactoryBot.create(:user).tap do |user|
group = FactoryBot.create(:group, name: 'Testers')
user.groups << group
login_user user
end
end
it "returns 200" do
get become_a_member_path
expect(response.status).to eq 200
end
end
end
end
Loading the module this way:
config.extend ControllerMacros, :type => :controller
should allow you to use the login_user
method in your controller specs.
If it comes to a point where this is annoying to repeat, you can optionally do this:
module ControllerMacros
def login_user
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryBot.create(:user)
sign_in user
end
end
end
and simply call the method in your controller spec:
RSpec.describe ApplicationFunnelController do
login_user
# Tests here...
end
You can find more about testing Devise with Rspec in their repository wiki: