Подтвердить что ты не робот

Factory Девушка /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 [email protected]:~/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 [email protected]:~/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 ничего не удаляет. Поэтому я более или менее вернусь к квадрату.

4b9b3361

Ответ 1

С помощью списка рассылки 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

Ответ 2

Я думаю, что пользовательская переменная в 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

Ответ 3

Это не технически ответ, скорее комментарий, а уточнение кода - самый простой механизм.

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

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 definitely there before page load
puts User.first

    #line two
    visit '/sessions/index'

# check the user 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 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, что является хорошим быстрым способом для получения чистой сессии.

Ответ 4

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

https://github.com/jnicklas/capybara

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

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

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