There are already a few questions on stackoverflow with possible solutions, but none of them seemed to solve my problem, thus I'm asking another one, in hope it's specific enough to not be closed as duplicate.
I am trying to test a customized login form that uses devise. The environment consists of
The following spec is failing due to a 401 Unauthorized
response, rendering the login form again with an error message that the credentials were wrong.
require 'rails_helper'
feature 'Logging in' do
background do
FactoryBot.create :user, email: 'john.doe@example.com',
first_name: 'John',
password: 'password',
password_confirmation: 'password'
end
scenario 'with correct credentials' do
visit new_user_session_path
within('#user-sessions--new') do
find('#user_email').fill_in with: 'john.doe@example.com'
find('#user_password').fill_in with: 'password'
end
find('input[name="commit"]').click
expect(page).to have_content 'John'
end
end
Checking the test.log
after temporarily disabling the parameter filters for logging, I can see that the password has been submitted properly, and the user is being created before the login attempt happens, yet the authentication fails. Entering an interactive debugging session and trying to log in manually inside the capybara's spun up browser, the login also fails. The email/password login works in development mode when creating the same user through the console, though. Right now, no other devise features that could impact the login behavior (like confirmable
) are used.
Most of the questions I found so far advise the following points:
Disable transactional fixtures: My rails_helper.rb
already contains this;
RSpec.configure do |config|
config.use_transactional_fixtures = false
end
Configure Database Cleaner: This was suggested in Failing to test Devise with Capybara and was also already the case before I started to implement capybara specs;
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(
:truncation,
except: %w[ar_internal_metadata schema_migrations]
)
end
config.before :each do
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
Right now I have no other Idea why this scenario could be failing except for maybe the environment in which the tests are run hot having a proper database connection. If someone has an idea that helps, I'd be happy to hear it.
When using Rails > 5.0 it safely shares the database connection between the tests and the app under test Capybara starts. Because of that you don't need database cleaner, and you should be using transactional tests for the speed and isolation benefits. Start by removing all references to database cleaner from your project and re-enabling transactional fixtures (the default). If that doesn't solve your issue then there's a couple of other potential issues.
Your factory isn't creating a valid user - Use the FactoryBot.lint functionality to verify all your factories produce valid objects before your tests - https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#linting-factories
You have a JS error on your page that is causing issues. In development mode each JS asset is served separately which means an error in one doesn't affect the others. In test mode, however, the assets are all concatenated which means an error in one can cause JS in other assets not to be run. Check your browser console for any JS errors and fix them.
If none of that helps please add relevant portions of your test.log to your question
Note: You're writing your tests more verbosely than they need to be. If locating fields to fill_in by id you can just pass the id to fill_in
and not need to use separate find
calls for every element, you should also use the more semantic click_button
when the element you're attempting to click qualifies as a button
within('#user-sessions--new') do
fill_in 'user_email', with: 'john.doe@example.com'
fill_in 'user_password', with: 'password'
end
click_button('commit')
expect(page).to have_content 'John'