34

我正在尝试使用 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)

运行第一组测试很好:

spec spec/ 


...
Finished in 2.758806 seconds

60 examples, 0 failures, 11 pending

一切都很好,正如预期的那样,但是再次运行测试:

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

修复尝试 - 使用 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

然后我跑了

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

用户似乎保留在数据库中

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

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

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

4

5 回答 5

52

终于解决了这个问题,我希望我可以为某人节省六个小时的调试时间。

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

令人窒息的测试

require 'spec_helper'

describe UsersController do

  @user = Factory.create(:user) 
end

测试有效

require 'spec_helper'

describe UsersController do

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

事务由它“应该做某事”做...声明定义。如果您在该语句之外实例化工厂,则事实证明它不是事务性的。

你也可以把它放在“它应该..”块之外,只要它在“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

在试验中,在“它应该做..end”块之外定义用户似乎是有效的,只要它在“before..end”块中。我猜这仅在“它应该做..end”块的范围内执行,因此工作正常。

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

于 2010-08-17T02:45:32.027 回答
21

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

于 2010-12-08T18:43:32.047 回答
4

spec/spec_helper.rb中,请确保您具有以下内容

RSpec.configure do |config|
  config.use_transactional_fixtures = true
end

这似乎解决了我的问题。

于 2011-09-15T21:51:43.857 回答
3

在里面test/test_helper.rb确保你有以下内容。

class ActiveSupport::TestCase
  self.use_transactional_fixtures = true
  #...
end

尽管名称为“fixtures”,但factory_girl它也适用。

于 2010-08-16T21:16:53.370 回答
0

在将项目从 Rails 3 升级到 Rails 4 时,我遇到了这些相同的症状。我已经完成了捆绑安装,开发模式似乎工作正常,但我不会在测试中获得事务行为。事实证明,进行捆绑更新解决了这个问题。

于 2013-07-12T00:57:49.443 回答