Factory Girl / Capybara удаляет записи из базы данных в середине теста?
Работая с RSpec & Capybara, я получаю интересный режим неудачного тестирования, который исчезает с несколькими тонкими перестановками строк в тестовом примере... вещи, которые не должны иметь значения.
Я разрабатываю свою собственную систему аутентификации. В настоящее время он работает, и я могу войти / выйти из браузера, сеанс работает и т. Д. И т. Д. Однако попытка проверить это не удалась. Что-то происходит, что я не совсем понимаю, что, кажется, зависит от порядка (казалось бы) несвязанных вызовов.
require 'spec_helper'
describe "Sessions" do
it 'allows user to login' do
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
#line two
visit '/sessions/index'
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
page.should have_content('Logged in')
end
end
Так как этот тест не пройден... вход не пройден. После вставки вызовов "отладчик" в спецификацию и контроллер я могу понять почему: пользователь не вставляется в базу данных в том, что касается контроллера:
Редактировать добавление в ApplicationController
class ApplicationController < ActionController::Base
helper :all
protect_from_forgery
helper_method :user_signed_in?, :guest_user?, :current_user
def user_signed_in?
!(session[:user_id].nil? || current_user.new_record?)
end
def guest_user?
current_user.new_record?
end
def current_user
@current_user ||= session[:user_id].nil? ? User.new : User.find(session[:user_id])
rescue ActiveRecord::RecordNotFound
@current_user = User.new
flash[:notice] = 'You\'ve been logged out.'
end
end
class SessionsController < ApplicationController
def login
user = User.where(:email=>params[:user][:email]).first
debugger ###
if !user.nil? && user.valid_password?(params[:user][:password])
#engage session
else
#run away
end
end
def logout
reset_session
redirect_to root_path, :notice => 'Logget Out.'
end
end
в консоли на вышеуказанной точке останова:
1.9.2 vox@Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
=> 0
ruby-1.9.2-p180 :002 >
Однако, если я переставлю несколько строк в своем тесте, поместив строку "два" над строкой "один":
describe "Sessions" do
it 'allows user to login' do
#line two
visit '/sessions/index'
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
page.should have_content('Logged in')
end
end
Я получаю это в консоли (та же точка останова, что и выше):
1.9.2 vox@Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
=> 1
Для краткости я опустил полный дамп содержимого пользовательского объекта, но могу вас заверить, что тест завершается, как и ожидалось.
Такое поведение перестановки строк для прохождения теста не очень хорошо согласуется с моим представлением о том, что должно происходить с этими командами, и оказалось весьма полезным для моего тестирования в других областях.
Есть намеки на то, что здесь происходит?
Я искал Google и SO для идей, которые представляют эту проблему, и нет недостатка в SO вопросах о RSpec/Capybara и Sessions. Ничто не казалось подходящим, хотя правильно.
Спасибо за поиск.
Обновить
Я добавил точку останова (непосредственно перед вызовом посещения) и некоторую отладку в тесте и вернулся с этим:
(rdb:1) user
#<User id: 1, login_count: 1, email: "testuser1@website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">
(rdb:1) User.all
[#<User id: 1, login_count: 1, email: "testuser1@website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">]
(rdb:1) next
/Users/vox/Sites/website/spec/controllers/sessions_controller_spec.rb:19
fill_in 'Email', :with => user.email
(rdb:1) User.all
[]
Совершенно ясно, что что-то в этом посещении говорит Factory Girl, что это сделано с пользовательским объектом, и она удаляет его?
Редактировать После тщательной проверки test.log, ничего не выдает никакого удаления. Так что я более или менее вернулся на круги своя.
4 ответа
С помощью списка рассылки Factory Girl я нашел проблему.
По умолчанию RSpec использует транзакции для поддержания базы данных в чистом состоянии, и каждая транзакция связана с потоком. Где-то вдоль конвейера команда visit_page отделяется, и транзакция, связанная с текущим потоком, умирает.
Решение простое: отключить транзакции.
describe "Sessions" do
self.use_transactional_fixtures = false
it 'no longer uses transactions' do
#whatever you want
end
end
Обновление для Rails 5.1
Начиная с Rails 5.1, use_transactional_fixtures
устарела и должна быть заменена use_transactional_tests
,
self.use_transactional_tests = false
Я думаю, что переменная пользователя в RSpec перезаписала переменную в контроллере, чтобы она не работала? (не удалось получить правильный user.email в тесте)
До:
user = Factory(:user)
user.password! '2468'
visit '/sessions/index' # user gets overwritten
fill_in 'Email', :with => user.email # can't get user.email
После:
visit '/sessions/index' # Execute action
user = Factory(:user) # user gets overwritten
user.password! '2468'
fill_in 'Email', :with => user.email # user.email works
Технически это не ответ, а скорее комментарий, но для пояснения кода это самый простой механизм.
Можете ли вы попробовать сделать следующее, чтобы помочь определить, где пользователь был уничтожен
describe "Sessions" do
it 'allows user to login' do
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
# check the user's definitely there before page load
puts User.first
#line two
visit '/sessions/index'
# check the user's still there after page load
puts User.first.reload
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
# check the user's still there on submission (though evidently not)
puts User.first.reload
page.should have_content('Logged in')
end
end
РЕДАКТИРОВАТЬ
Тот факт, что он работает для вас нормально в реальной жизни, но не в Капибаре, предполагает, что он может быть продуктом существующей информации о сеансе. Когда вы тестируете в браузере, вы обычно отходите от предыдущей работы, но Capybara всегда начинается с чистого сеанса.
Вы можете легко увидеть, можете ли вы воспроизвести ошибку Capybara в браузере, очистив все свои куки (как я уверен, вы знаете) или просто переключившись на новое окно инкогнито в Chrome/FF, что является хорошим быстрым способом получить чистая сессия.
Правильный ответ выше помог мне. Конечно, мне нужно было изменить некоторые другие тесты, которые (ошибочно или неправильно) предполагали, что приспособление не существует. Для получения дополнительной информации: есть некоторая информация об этом в README Capybara.
https://github.com/jnicklas/capybara
"Если вы используете базу данных SQL, обычно каждый тест выполняется в транзакции, откат которой выполняется в конце теста, например, rspec-rails делает это по умолчанию из коробки. Так как транзакции обычно не выполняются разделение между потоками, это сделает данные, которые вы поместили в базу данных в тестовом коде, невидимыми для Capybara."
Вы также можете настроить RSpec для очистки после теста вручную:
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Cleanup-after-your-Rspec-tests