ruby on rails - rails - Agilización de asociaciones en las especificaciones del modelo con FactoryGirl-crear vs compilación vs compilación_estimular
rspec factory bot (4)
Si no desea que sus pruebas lleguen a la base de datos, esto es lo que tendría que hacer.
before do
@user = FactoryGirl.build_stubbed :user
@post = FactoryGirl.build_stubbed :post
@user.stub(:posts).and_return([@post])
@post.stub(:user).and_return(@user)
end
Nota: Tenga cuidado al usar before(:all)
. No se ejecuta en una transacción. Entonces, cualquier cosa que cree before(:all)
quedará atrás en la base de datos y podría causar conflicto con otras pruebas
Acerca de FactoryGirl.build
, construye el objeto, pero crea las asociaciones.
Por ejemplo:
factory :user do
association posts
end
FactoryGirl.build(:user) #this creates posts in the database even though you are only building the parent object(user)
Digamos que tengo modelos de User
y Post
, un usuario tiene has_many
publicaciones y una publicación belongs_to
un usuario.
Cuando escribo una especificación para Post
, mi primer instinto es escribir algo como esto:
before do
@user = FactoryGirl.create :user
@post = @user.posts.new(title: "Foo", content: "bar)
end
... tests for @post go here ...
Pero esto creará un nuevo usuario, que accederá a la base de datos, para cada prueba, lo que ralentizará las cosas. ¿Hay una forma mejor de hacer esto que acelerará mis pruebas y evitar golpear el DB tan a menudo?
Según tengo entendido, no puedo usar FactoryGirl.build :user
porque, aunque no llegue al DB, las asociaciones no funcionarán correctamente porque @user
no tendrá una ID y por lo tanto @post.user
ganó no funciona (devuelve nada)
Podría usar FactoryGirl.build_stubbed :user
que creó un @post.user
FactoryGirl.build_stubbed :user
"falso persistente" que tiene una ID, pero @post.user
aún devuelve nil. ¿ build_stubbed
tiene alguna ventaja práctica sobre build
cuando estoy probando cosas relacionadas con asociaciones?
Supongo que podría usar build_stubbed
stub @post.user
para que devuelva @user
... ¿hay alguna razón por la que esta sea una mala idea?
¿O debería usar create
y aceptar el golpe de velocidad?
La única alternativa que se me ocurre sería configurar @user en un bloque before(:all)
que parece una mala idea.
¿Cuál es la mejor manera de escribir este tipo de pruebas de una manera limpia y concisa que evite hacer demasiadas consultas DB?
Respuesta corta
@user = FactoryGirl.build_stubbed(:user)
@post = FactoryGirl.build_stubbed(:post, :user => @user)
Esto hará que @ post.user funcione sin golpear la base de datos.
Respuesta larga
Mi recomendación sería esperar en el bloque before
hasta que esté seguro de que lo necesita. En su lugar, cree los datos que necesita para cada prueba individual y extraiga la duplicación en métodos o fábricas nuevas a medida que lo encuentre.
Además, ¿realmente necesita hacer referencia al usuario en cada prueba individual? Tener @user
disponible en cada prueba le dice a otros desarrolladores que es importante en todas partes.
Por último, suponiendo que la asociación de usuarios también se declare en la fábrica de correos, obtendrás automáticamente un post.user
funcionamiento cuando post.user
build_stubbed(:post)
.
Puede ser fácil olvidar las diferencias entre create
, build
y build_stubbed
. Aquí hay una referencia rápida para quienes se encuentran en la misma situación (ya que esta página tiene una posición muy alta en los resultados de búsqueda).
# Returns a User instance that''s not saved (does not write to DB)
user = build(:user)
# Returns a saved User instance (writes to DB)
user = create(:user)
# Returns a hash of attributes that can be used to build a User instance
attrs = attributes_for(:user)
# Returns an object with all defined attributes stubbed out
stub = build_stubbed(:user)
# Passing a block to any of the methods above will yield the return object
create(:user) do |user|
user.posts.create(attributes_for(:post))
end
Del documento de factory girl, puedes identificar la build
estrategias para el user
en asociación en post
fábrica post
como esta:
factory :post do
association :user, factory: :user, strategy: :build
end
Para que pueda build
una post
sin guardar user
post = build(:post)
post.new_record? # => true
post.author.new_record? # => true