为什么factory_girl不能为我进行交易操作? - 测试后行仍保留在数据库中

Pet*_*xey 34 ruby-on-rails factory-bot

我正在尝试使用factory_girl创建一个"用户"工厂(使用RSpec)但是它似乎没有在事务上运行,并且由于测试数据库中先前测试的残余数据而显然失败了.

Factory.define :user do |user|
  user.name                   "Joe Blow"
  user.email                  "joe@blow.com" 
  user.password               'password'
  user.password_confirmation  'password'
end

@user = Factory.create(:user)
Run Code Online (Sandbox Code Playgroud)

运行第一组测试很好:

spec spec/ 


...
Finished in 2.758806 seconds

60 examples, 0 failures, 11 pending
Run Code Online (Sandbox Code Playgroud)

一切都很好并且符合预期,但是再次运行测试:

spec spec/ 
...
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/validations.rb:1102:in `save_without_dirty!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/dirty.rb:87:in `save_without_transactions!'
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:200:in `save!'
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction'
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:182:in `transaction'
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:200:in `save!'
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:208:in `rollback_active_record_state!'
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:200:in `save!'
    from /Library/Ruby/Gems/1.8/gems/factory_girl-1.2.3/lib/factory_girl/proxy/create.rb:6:in `result'
    from /Library/Ruby/Gems/1.8/gems/factory_girl-1.2.3/lib/factory_girl/factory.rb:316:in `run'
    from /Library/Ruby/Gems/1.8/gems/factory_girl-1.2.3/lib/factory_girl/factory.rb:260:in `create'
    from /Users/petenixey/Rails_apps/resample/spec/controllers/users_controller_spec.rb:7
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/example/example_group_methods.rb:183:in `module_eval'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/example/example_group_methods.rb:183:in `subclass'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/example/example_group_methods.rb:55:in `describe'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/example/example_group_factory.rb:31:in `create_example_group'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/dsl/main.rb:28:in `describe'
    from /Users/petenixey/Rails_apps/resample/spec/controllers/users_controller_spec.rb:3
    from /Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:147:in `load_without_new_constant_marking'
    from /Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:147:in `load'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/runner/example_group_runner.rb:15:in `load_files'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/runner/example_group_runner.rb:14:in `each'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/runner/example_group_runner.rb:14:in `load_files'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/runner/options.rb:133:in `run_examples'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/lib/spec/runner/command_line.rb:9:in `run'
    from /Library/Ruby/Gems/1.8/gems/rspec-1.3.0/bin/spec:5
    from /usr/bin/spec:19:in `load'
    from /usr/bin/spec:19
Run Code Online (Sandbox Code Playgroud)

修复尝试 - 使用Factory.sequence

由于我的电子邮件字段中有唯一性约束,因此我尝试使用factory_girl的序列方法解决问题:

Factory.define :user do |user|
  user.name                   "Joe Blow"
  user.sequence(:email) {|n| "joe#{n}@blow.com" }
  user.password               'password'
  user.password_confirmation  'password'
end
Run Code Online (Sandbox Code Playgroud)

然后我跑了

rake db:test:prepare
spec spec/
.. # running the tests once executes fine
spec spec/
.. # running them the second time produces the same set of errors as before
Run Code Online (Sandbox Code Playgroud)

用户似乎仍留在数据库中

如果我查看/db/test.sqlite3数据库,似乎测试用户的行没有在测试之间从数据库回滚.我认为这些测试应该是交易性的,但对我来说似乎并非如此.

这可以解释为什么测试第一次正确运行(如果我清除数据库)但第二次失败.

谁能解释我应该改变什么以确保测试以事务方式运行?

Pet*_*xey 52

最后解决这个问题,我希望我可以节省六个小时的调试时间,这让我想出来了.

通过a)变得幸运并最终得到一个有效的代码版本和b)剥离这两组代码,这就是我发现的:

测试窒息

require 'spec_helper'

describe UsersController do

  @user = Factory.create(:user) 
end
Run Code Online (Sandbox Code Playgroud)

测试有效

require 'spec_helper'

describe UsersController do

  it "should make a factory models without choking" do
    @user = Factory.create(:user)   
  end
end
Run Code Online (Sandbox Code Playgroud)

该事务由它"应该做某事"做...声明.如果您在该声明之外实例化工厂,则结果不是事务性的.

你也可以将它放在"it should .."块之外,只要它在"before..end"块中

require 'spec_helper'

describe UsersController do

  before(:each) do
    @user = Factory.create(:user) 
  end

  it 'should make a factory without choking' do
    puts @user.name
    # prints out the correnct name for the user
  end
end
Run Code Online (Sandbox Code Playgroud)

在实验中,只要它在"before ... end"块中,在"it should do..end"块之外定义用户似乎是有效的.我想这只是在"it should do..end"块的范围内执行,因此工作正常.

[感谢@jdl提出的(也是正确的)建议]

  • 关于这在let中的表现如何?像这样:让(:某事){工厂(:某事)} (6认同)
  • 非常有帮助,谢谢彼得.塞扎尔,重新.`let(:foo){Factory(:foo)}`,看起来`foo`将在第一次引用时被创建(工厂调用),所以它是对foo的引用必须出现在一个例子或`之前`块.DB仍在示例结尾处回滚. (2认同)
  • 如果你想立即创建foo,你也可以使用`let!`.(是的,这确实适用于交易.) (2认同)

Mar*_*den 21

请参阅我的博客文章,了解使用before :allbefore :each关于交易的区别:http://mwilden.blogspot.com/2010/11/beware-of-rspecs-before-all.html.简而言之,before :all不是事务性的,并且在测试运行后,在那里创建的数据将会存在.