Factory Girl / Capybara удаляет записи из базы данных в середине теста? - PullRequest
14 голосов
/ 12 августа 2011

Работая с 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 ]

23 голосов
/ 18 августа 2011

С помощью списка рассылки 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
3 голосов
/ 17 августа 2011

Я думаю, что переменная пользователя в 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
1 голос
/ 17 августа 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 в браузере, выполнивочистить все ваши куки (как я уверен, вы знаете) или просто переключиться на новое окно инкогнито в Chrome / FF, что является хорошим быстрым способом получить чистую сессию.

0 голосов
/ 15 апреля 2013

Правильный ответ выше помог мне.Конечно, мне нужно было изменить некоторые другие тесты, которые (ошибочно или неправильно) предполагали, что приспособление не существует.Для получения дополнительной информации: есть некоторая информация об этом в README Capybara.

https://github.com/jnicklas/capybara

"Если вы используете базу данных SQL, обычно выполняется каждый тест в транзакции, которыйоткатывается в конце теста, rspec-rails делает это по умолчанию, например, из коробки.Так как транзакции обычно не разделяются между потоками, это сделает данные, которые вы поместили в базу данных в вашем тестовом коде, невидимымина Капибару. "

Вы также можете настроить RSpec для очистки после теста вручную:

https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Cleanup-after-your-Rspec-tests

...