2010-07-29 18 views
24

Siempre que tengo una fábrica de proyectos¿Cómo puedo restablecer una secuencia factory_girl?

Factory.define :project do |p| 
    p.sequence(:title) { |n| "project #{n} title"     } 
    p.sequence(:subtitle) { |n| "project #{n} subtitle"    } 
    p.sequence(:image) { |n| "../images/content/projects/#{n}.jpg" } 
    p.sequence(:date)  { |n| n.weeks.ago.to_date     } 
end 

Y que estoy creando instancias de proyecto

Factory.build :project 
Factory.build :project 

En ese momento, la próxima vez que ejecute Factory.build (: Proyecto) I' Recibirá una instancia de Proyecto con un título configurado para "título de proyecto 3" y así sucesivamente. No es sorprendente.

Ahora digo que deseo restablecer mi contador dentro de este alcance. Algo como:

Factory.build :project #=> Project 3 
Factory.reset :project #=> project factory counter gets reseted 
Factory.build :project #=> A new instance of project 1 

¿Cuál sería la mejor manera de lograrlo?

actualmente estoy usando las siguientes versiones:

factory_girl (1.3.1) factory_girl_rails (1.0)

Gracias de antemano, Saludos.

+0

Hola, uno puede decirme dónde se agregó el archivo de fábrica .rb. En el archivo de características o en el archivo de especificaciones. – Selvamani

Respuesta

9

Hola a todos, Después de rastrear mi camino a través del código fuente, finalmente he encontrado una solución para esto. Si está utilizando factory_girl 1.3.2 (que era la versión más reciente en el momento en que escribo esto), se puede añadir el siguiente código al principio del archivo factories.rb:

class Factory 
    def self.reset_sequences 
    Factory.factories.each do |name, factory| 
     factory.sequences.each do |name, sequence| 
     sequence.reset 
     end 
    end 
    end 

    def sequences 
    @sequences 
    end 

    def sequence(name, &block) 
    s = Sequence.new(&block) 

    @sequences ||= {} 
    @sequences[name] = s 

    add_attribute(name) { s.next } 
    end 

    def reset_sequence(name) 
    @sequences[name].reset 
    end 

    class Sequence 
    def reset 
     @value = 0 
    end 
    end 
end 

Luego, en env.rb de pepino, sólo tiene que añadir:

After do 
    Factory.reset_sequences 
end 

me asumir si llegas a tener el mismo problema en sus pruebas rspec, se puede usar después de rspecs: cada método.

Por el momento, este enfoque sólo tiene en cuenta las secuencias definidas dentro de una fábrica, tales como:

Factory.define :specialty do |f| 
    f.sequence(:title) { |n| "Test Specialty #{n}"} 
    f.sequence(:permalink) { |n| "permalink#{n}" } 
end 

todavía no he escrito el código para manejar: Factory.sequence ...

Espero que esto ayude a todas las otras personas frustradas que no pueden entender por qué en el mundo las chicas de las fábricas ya no ofrecen esto. Tal vez bifurque el proyecto github y envíe una solicitud de extracción con esta solución, ya que no cambia ninguna de sus funciones internas.

-Andrew

+0

Hola, cualquiera me puede decir dónde puedo agregar el archivo de fábrica.rb. En el archivo de características o en el archivo de especificaciones. – Selvamani

1

No hay está construido en forma de reiniciar una secuencia, ver el código fuente aquí:.

http://github.com/thoughtbot/factory_girl/blob/master/lib/factory_girl/sequence.rb

Sin embargo, algunas personas han hackeado/mono-parcheado esta característica en He aquí un ejemplo:

http://www.pmamediagroup.com/2009/05/smarter-sequencing-in-factory-girl/

+0

Hola, he estado al acecho de su fuente también y también encontré esa publicación que vinculó. Todavía no he resuelto exactamente cómo resolver mi problema. Factory.sequences se comporta [aparentemente] de una manera extraña. Factory.build (: project) # => project 1 Factory.build (: project) # => project 2 Factory.sequences.delete (: project) Factory.build (: project) # => project 3 Todavía estoy tratando de descubrir por qué. – DBA

+0

La secuencia le proporciona aquí una identificación única garantizada, no el recuento de los objetos. Ese comportamiento es exactamente lo que deberías esperar. Estos se utilizan para garantizar que se genere un nombre único cada vez que realice una instancia de fábrica. – Winfield

+1

Eso es exactamente lo que intento restablecer :) – DBA

6

Según ThoughBot Here, la necesidad de restablecer la secuencia entre las pruebas es un anti-patrón.

Para summerize:

Si usted tiene algo como esto:

FactoryGirl.define do 
    factory :category do 
    sequence(:name) {|n| "Category #{n}" } 
    end 
end 

Sus pruebas deben tener este aspecto:

Scenario: Create a post under a category 
    Given a category exists with a name of "Category 1" 
    And I am signed in as an admin 
    When I go to create a new post 
    And I select "Category 1" from "Categories" 
    And I press "Create" 
    And I go to view all posts 
    Then I should see a post with the category "Category 1" 

NO: No

Scenario: Create a post under a category 
    Given a category exists 
    And I am signed in as an admin 
    When I go to create a new post 
    And I select "Category 1" from "Categories" 
    And I press "Create" 
    And I go to view all posts 
    Then I should see a post with the category "Category 1" 
+2

Me resulta muy útil para restablecer las secuencias entre las pruebas, así que ahora exactamente qué esperar de cada una y mis pruebas son más declarativas. – Macario

38

Simplemente llame a FactoryGirl.reload en su devolución de llamada antes/después. Esto se define en el código base factorygirl como:

module FactoryGirl 
    def self.reload 
    self.factories.clear 
    self.sequences.clear 
    self.traits.clear 
    self.find_definitions 
    end 
end 

llamando FactoryGirl.sequences.clear no es suficiente por alguna razón. Hacer una recarga completa puede tener un poco de sobrecarga, pero cuando lo intenté con/sin la devolución de llamada, mis pruebas tomaron alrededor de 30 segundos para ejecutarse de cualquier manera. Por lo tanto, la sobrecarga no es suficiente para afectar mi flujo de trabajo.

+0

Gracias, funciona. Agregué este código en mi spec-> support-> reset.rb y agregué 'After do FactoryGirl.reload end' esto en el archivo env.rb – Selvamani

9

Para las personas googlear: sin extender aún más, acaba de hacer FactoryGirl.reload

FactoryGirl.create :user 
#=> User id: 1, name: "user_1" 
FactoryGirl.create :user 
#=> User id: 2, name: "user_2" 

DatabaseCleaner.clean_with :truncation #wiping out database with truncation 
FactoryGirl.reload 

FactoryGirl.create :user 
#=> User id: 1, name: "user_1" 

obras para mí en

* factory_girl (4.3.0) 
* factory_girl_rails (4.3.0) 

https://stackoverflow.com/a/16048658

0

Para restablecer secuencia particular puede probar

# spec/factories/schedule_positions.rb 
FactoryGirl.define do 
    sequence :position do |n| 
    n 
    end 

    factory :schedule_position do 
    position 
    position_date Date.today 
    ... 
    end 
end 

# spec/models/schedule_position.rb 
require 'spec_helper' 

describe SchedulePosition do 
    describe "Reposition" do 
    before(:each) do 
     nullify_position 
     FactoryGirl.create_list(:schedule_position, 10) 
    end 
    end 

    protected 

    def nullify_position 
    position = FactoryGirl.sequences.find(:position) 
    position.instance_variable_set :@value, FactoryGirl::Sequence::EnumeratorAdapter.new(1) 
    end 
end 
+1

En la versión 4.5.0, logré hacerlo usando' FactoryGirl .configuration.sequences [: iterator] .instance_variable_get ("@ value"). instance_variable_set ("@ value", 1) '(where ': iterator' es el nombre de la secuencia). Tenga en cuenta que esto solo funciona en secuencias globales. –

2

Debes asegurarte de que las secuencias van del 1 al 8 y reiniciar a 1, y así sucesivamente. Implementado como esto:

class FGCustomSequence 
    def initialize(max) 
    @marker, @max = 1, max 
    end 
    def next 
    @marker = (@marker >= @max ? 1 : (@marker + 1)) 
    end 
    def peek 
    @marker.to_s 
    end 
end 

FactoryGirl.define do 
    factory :image do 
    sequence(:picture, FGCustomSequence.new(8)) { |n| "image#{n.to_s}.png" } 
    end 
end 

El doc dice "El valor sólo tiene que apoyar el método #next." Pero para mantener su objeto CustomSequence en proceso, también debe admitir el método #peek. Por último, no sé cuánto tiempo funcionará esto porque pirateó las partes internas de FactoryGirl, cuando hacen un cambio esto puede no funcionar correctamente.

0

Si está usando Cucumber puede agregar esto a una definición de paso:

Given(/^I reload FactoryGirl/) do 
    FactoryGirl.reload 
end 

Entonces simplemente llámalo cuando sea necesario.

Cuestiones relacionadas