Почему factory_girl не работает для меня транзакционно?- строки остаются в базе данных после тестов - PullRequest
34 голосов
/ 17 августа 2010

Я пытаюсь использовать 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, мне кажется, что строка для тестового пользователя не откатывается из базы данных между тестами. Я думал, что эти тесты должны были быть транзакционными, но, похоже, они мне не подходят.

Это объясняет, почему тест выполняется правильно в первый раз (и если я очищаю базу данных), но не проходит во второй раз.

Может кто-нибудь объяснить, что я должен изменить, чтобы обеспечить выполнение транзакций транзакционно?

Ответы [ 5 ]

52 голосов
/ 17 августа 2010

Наконец-то исправили это, и я надеюсь, что смогу спасти кого-то шесть часов отладки, которые потребовались мне, чтобы понять это.

Из-за: а) удачи и в конечном итоге полученной версии кода, и б) удаления обоих наборов кода, вот что я нашел:

Тест, который душит

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

Транзакция определяется оператором , который «должен что-то делать» do ... . Если вы создаете экземпляр фабрики вне этого оператора, он оказывается не транзакционным.

Вы также можете поместить его вне блока «оно должно ..», если оно находится в блоке «до… конца»

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

При экспериментировании кажется правильным определить пользователя вне блока «он должен сделать .. конец», если он находится в блоке «до… конца». Я предполагаю, что это выполняется только в области блока "оно должно сделать .. конец" и поэтому работает нормально.

[Спасибо @jdl за его (тоже правильное) предложение]

21 голосов
/ 08 декабря 2010

См. Мою запись в блоге о разнице между использованием before :all и before :each в отношении транзакций: http://mwilden.blogspot.com/2010/11/beware-of-rspecs-before-all.html. В двух словах, before :all не является транзакционным, и созданные там данные останутся после тест запущен.

4 голосов
/ 16 сентября 2011

В spec/spec_helper.rb, убедитесь, что у вас есть следующее

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

Это, кажется, решает проблему для меня.

3 голосов
/ 17 августа 2010

Внутри test/test_helper.rb убедитесь, что у вас есть следующее.

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

Несмотря на название "fixtures", это работает и с factory_girl.

0 голосов
/ 12 июля 2013

Я столкнулся с этими же симптомами при обновлении проекта с Rails 3 до Rails 4. Я выполнил пакетную установку, и режим разработки, казалось, работал нормально, но в тестах поведение транзакций не получалось.Оказывается, обновление пакета решило проблему.

...