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: "[email protected]", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">
(rdb:1) User.all
[#<User id: 1, login_count: 1, email: "[email protected]", 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 ничего не удаляется. Так что я более или менее вернулся к исходной точке.


person voxobscuro    schedule 12.08.2011    source источник


Ответы (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
person voxobscuro    schedule 18.08.2011
comment
Лучше всего настраивать в spec_helper.rb. - person iltempo; 28.03.2012
comment
Это поставило меня в тупик на весь день, так как у меня было много отвлекающих факторов, которые вели меня по ложному пути. Спасибо! - person Rimian; 14.04.2013

Я думаю, что пользовательская переменная в RSpec перезаписала переменную в контроллере, поэтому она не сработала? (не удалось получить правильный адрес электронной почты пользователя в тесте)

До :

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
person Cydonia7    schedule 16.08.2011
comment
Я не думаю, что это проблема с перезаписью пользовательской переменной, но я займусь этим. Спасибо. - person voxobscuro; 17.08.2011

Технически это не ответ, а скорее комментарий, но для пояснения кода это самый простой механизм.

Можете ли вы попробовать сделать следующее, чтобы сузить круг уничтожения пользователя?

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 всегда начинает с чистого сеанса.

Вы можете легко увидеть, можете ли вы воспроизвести ошибку Capybara в браузере, очистив все свои файлы cookie (я уверен, что вы знаете) или просто переключившись на новое окно в режиме инкогнито в Chrome / FF, что является хорошим быстрым способом получить чистая сессия.

person Peter Nixey    schedule 16.08.2011
comment
Что ж, спасибо за совет ... Теперь я знаю немного больше о проблеме, но все еще не могу разобраться. - person voxobscuro; 18.08.2011
comment
Можете ли вы опубликовать код метода, который отвечает на visit '/sessions/index', предположительно sessions_controller#index (возможно, стоит подумать о том, чтобы поместить это на sessions/new кстати), а также на контроллер вашего приложения. - person Peter Nixey; 18.08.2011
comment
Теперь вы определили, что проблема возникает в индексе сеансов, тогда просто использование этих puts User.last точек отладки в коде должно позволить вам точно увеличить масштаб, где удаляется пользователь. Я не думаю, что FactoryGirl удаляет это BTW - я не думаю, что FactoryGirl может удалять вещи - person Peter Nixey; 18.08.2011
comment
session / index - это просто представление, и это просто форма. Действие контроллера не выполняется. Я заполнил некоторые более важные части методов sessions_controller # login и #logout и добавил их в ApplicationController. Ничего особенного там не происходит. - person voxobscuro; 18.08.2011

Правильный ответ выше мне помог. Конечно, мне нужно было изменить некоторые другие тесты, которые (ошибочно или правильно) предполагали, что приспособления не существует. Для получения дополнительной информации: есть некоторая информация об этом в 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

person Rimian    schedule 15.04.2013