Factory Girl / Capybara从数据库测试中删除记录?

与RSpec和Capybara合作,我得到了一个有趣的测试失败模式,在测试用例中有一些细微的重排线……这些都不重要。

我正在开发自己的身份validation系统。 它目前正在工作,我可以登录/退出浏览器和会话工作等等。但是,尝试测试这是失败的。 正在发生的事情我不太明白,这似乎取决于(看似)不相关的电话的顺序。

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 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 

为了简洁起见,我省略了用户对象内容的完全转储,但我可以向您保证测试按预期完成。

交换行以使测试通过的这种行为并不能很好地适应我对这些命令应该发生什么的想法,并且已经certificate对我在其他领域的测试非常重要。

关于这里发生了什么的任何暗示?

我已经搜索谷歌和SO的想法提出这个问题,并且不乏关于RSpec / Capybara和Sessions的SO问题。 似乎没有什么比较合适了。

谢谢你的期待。

更新

我添加了一个断点(在访问调用之前)和一些调试到测试并返回:

 (rdb:1) user # (rdb:1) User.all [#] (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后,没有任何内容发出任何删除。 所以我或多或少地回到原点。

在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总是从干净的会话开始。

您可以通过清除所有Cookie(我确定您知道)或只需切换到Chrome / FF中的新隐身窗口即可轻松查看是否可以在浏览器中重现Capybara错误,这是一种快速获取干净的会议。

上面的正确答案对我有帮助。 当然,我需要更改一些其他测试(错误或正确)假设夹具不存在。 有关更多信息:在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