2010-08-16 10 views
34

Estoy tratando de usar factory_girl para crear una fábrica de "usuario" (con RSpec) sin embargo, no parece estar operando transaccionalmente y aparentemente está fallando debido a datos remanentes de pruebas previas en el base de datos de prueba.¿Por qué factory_girl no funciona transaccionalmente para mí? - las filas permanecen en la base de datos después de las pruebas

Factory.define :user do |user| 
    user.name     "Joe Blow" 
    user.email     "[email protected]" 
    user.password    'password' 
    user.password_confirmation 'password' 
end 

@user = Factory.create(:user) 

Ejecución de la primera serie de pruebas está muy bien:

spec spec/ 


... 
Finished in 2.758806 seconds 

60 examples, 0 failures, 11 pending 

Todo bien y como se esperaba, sin embargo ejecutar las pruebas de nuevo: intento

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 

Fix - utilizar Factory.sequence

Como tengo una singularidad const Raint en mi campo de correo electrónico que intentó solucionar el problema utilizando el método de secuencia de 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 

entonces me encontré con

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 

usuarios parecen permanecer en la base de datos

Si Cuando miro la base de datos /db/test.sqlite3, parece que la fila para el usuario de prueba no se está retirando de la base de datos entre las pruebas. Pensé que se suponía que estas pruebas serían transaccionales, pero no parecen ser así para mí.

Esto explicaría por qué la prueba se ejecuta correctamente la primera vez (y si borro la base de datos) pero falla por segunda vez.

¿Alguien puede explicar lo que debería cambiar para asegurarse de que las pruebas se ejecutan de forma transaccional?

+0

¿Puedo sugerir que se eliminen las menciones de factory-girl en la pregunta y la etiqueta? Dado que el problema central realmente se trata de crear registros en el bloque equivocado. Lo mismo puede suceder si se crea un registro mediante la llamada create() incorporada. – lulalala

Respuesta

52

Finalmente lo solucioné y espero poder salvar a alguien las seis horas de depuración que me llevó a resolverlo.

por: a) tener suerte y terminando con una versión de código que funcionó y b) separar los dos conjuntos de código de abajo esto es lo que encontré:

prueba que ahoga

require 'spec_helper' 

describe UsersController do 

    @user = Factory.create(:user) 
end 

prueba que funciona

require 'spec_helper' 

describe UsersController do 

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

la transacción se define por el "debería hacer algo" do ... declaración. Si instancia la fábrica fuera de esa declaración, resulta que no es transaccional.

También se puede poner fuera de la "Debería .." bloque, siempre y cuando sea en un bloque "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 

En la experimentación, que parece ser válida para definir un usuario fuera de un bloque "debería hacer ... fin", siempre que esté en un bloque "antes ... fin". Supongo que esto solo se ejecuta en el alcance del bloque "debería hacer ... fin" y, por lo tanto, funciona bien.

[Gracias a @jdl por su sugerencia (también correcto)]

+6

alguna idea sobre cómo se comporta esto dentro de un let? como tal: let (: something) {Factory (: something)} – Cezar

+2

Muy útil, gracias Peter. Cezar, re. 'let (: foo) {Factory (: foo)}', parece que 'foo' se creará (la fábrica se invoca) cuando se hace referencia por primera vez, por lo que es la referencia a foo que debe aparecer dentro de un ejemplo o un' before' bloquear. La base de datos todavía se revierte al final del ejemplo. –

+2

También puede usar 'let!' Si desea que foo se cree inmediatamente. (Y sí, eso sí funciona con las transacciones). – Ajedi32

3

Dentro de test/test_helper.rb asegúrese de tener lo siguiente.

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

A pesar de los "accesorios" nombre Esto funciona con factory_girl también.

+1

Aunque parece correcto, eso no parece solucionar el problema. Estoy trabajando a través del RailsTutorial de Michael Hartl y haciendo una segunda redacción del código que escribió originalmente. Cuando lo copié palabra por palabra todo funcionó bien y sé que nunca agregué el marcador user_transactional_fixtures. Ahora, aunque por alguna razón está fallando en el proyecto de reescritura, incluso si lo agrego. –

+0

Comentando aquí porque yo también vi el mismo comportamiento desconcertante. De lo contrario, el tuorial fue increíble. –

21

Ver mi entrada de blog en la diferencia entre el uso de before :all y before :each con respecto a las transacciones: http://mwilden.blogspot.com/2010/11/beware-of-rspecs-before-all.html. En pocas palabras, before :all no es transaccional, y los datos creados allí permanecerán después de que se ejecute la prueba.

+1

Una distinción importante, gracias por esa aclaración. Comprobando test.log, parece que el 'before 'all' todavía inicia una transacción (' BEGIN'), pero luego ejecuta un 'COMMIT' en lugar de' ROLLBACK', por lo que los datos persisten. –

+0

Muy útil, gracias –

4

En spec/spec_helper.rb, asegúrese de tener lo siguiente

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

Esto parece resolver el problema para mí.

0

Me encontré con estos mismos síntomas cuando actualicé un proyecto de Rails 3 a Rails 4. Hice una instalación de paquete, y el modo de desarrollo parecía funcionar bien, pero no obtuve el comportamiento transaccional en las pruebas. Resulta que al hacer una actualización de paquete se solucionó el problema.

Cuestiones relacionadas