2010-07-29 1 views
24

При условии, что у меня есть проект заводаКак сбросить последовательность 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 

И что я создаю экземпляры проекта

Factory.build :project 
Factory.build :project 

К этому времени, в следующий раз, когда я выполнить Factory.build (: проект) Я Получаем экземпляр проекта с названием «Project 3 title» и т. д. Неудивительно.

Теперь скажите, что я хочу сбросить счетчик в этой области. Что-то вроде:

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

Какой был бы лучший способ достичь этого?

настоящее время я использую следующие версии:

factory_girl (1.3.1) factory_girl_rails (1.0)

Спасибо заранее, С наилучшими пожеланиями.

+0

Привет, можно сказать, где вы добавили файл factory.rb. В файле функций или в файле spec. – Selvamani

ответ

9

Привет всем, После прослеживания моего пути через исходный код, я наконец нашел решение для этого. Если вы используете factory_girl 1.3.2 (который был самым последним релизом в то время, когда я пишу это), вы можете добавить следующий код в верхней части 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 

Тогда, в env.rb огурца, в просто добавьте:

After do 
    Factory.reset_sequences 
end 

Я полагаю, если вы столкнетесь с той же проблемой в ваших RSpec тестов, вы могли бы использовать rspecs после: каждый метода.

В настоящее время этот подход принимает во внимание только последовательности, определенных в пределах фабрики, такие как:

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

Я еще не написал код для обработки: Factory.sequence ...

Надеюсь, это поможет всем остальным разочарованным людям, которые не могут понять, почему в мире заводская девушка этого не дает. Возможно, я перекрою проект github и отправлю запрос на перенос с этим исправлением, поскольку он не изменяет ни одной из их внутренних функций.

-Эндрит

+0

Привет, кто-нибудь может сказать мне, где я могу добавить файл factory.rb. В файле функций или в файле spec. – Selvamani

1

Там нет встроенного способа сбросить последовательность, увидеть исходный код здесь:.

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

Однако некоторые люди взломали/обезьяну-заплату этой функции в этом не пример:

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

+0

Привет, я тоже скрывал их источник, а также нашел, что сообщение связано. Не совсем понял, как решить мою проблему. Factory.sequences ведет себя [по-видимому] странным образом. Factory.build (: project) # => проект 1 Factory.build (: project) # => проект 2 Factory.sequences.delete (: project) Factory.build (: project) # => проект 3 По-прежнему пытается понять, почему. – DBA

+0

Последовательность дает вам уникальный идентификатор здесь, а не количество объектов. Это то, что вы должны ожидать. Они используются, чтобы удостовериться, что имя генерируется уникально каждый раз, когда вы делаете экземпляр с фабрики. – Winfield

+1

Это именно то, что я пытаюсь сбросить :) – DBA

6

Согласно ThoughBot Here, необходимо сбросить последовательность между тестами является анти-паттерн.

Для summerize:

Если у вас есть что-то вроде этого:

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

Ваши тесты должны выглядеть следующим образом:

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" 

Не Это:

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

Мне очень полезно сбросить последовательности между тестами, поэтому теперь я точно знаю, что ожидать от каждого, а мои тесты более декларативные. – Macario

38

Просто позвоните в FactoryGirl.reload в вашем обратном вызове до/после. Это определено в кодовую FactoryGirl как:

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

Calling FactoryGirl.sequences.clear не является достаточным для какой-то причине. Выполнение полной перезагрузки может иметь некоторые накладные расходы, но когда я пытался с/без обратного вызова, мои тесты занимали около 30 секунд для запуска в любом случае. Поэтому накладных расходов недостаточно, чтобы повлиять на мой рабочий процесс.

+0

Спасибо, что работает. Я добавил этот код в свой файл spec-> support-> reset.rb и добавляю 'After do FactoryGirl.reload end' this в файле env.rb – Selvamani

9

Для погуглить людей: без дальнейшего расширения, просто сделать 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" 

работает для меня на

* factory_girl (4.3.0) 
* factory_girl_rails (4.3.0) 

https://stackoverflow.com/a/16048658

0

Для сброса определенной последовательности, вы можете попробовать

# 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

В версии 4.5.0 мне удалось это сделать, используя' FactoryGirl .configuration.sequences [: iterator] .instance_variable_get ("@ value"). instance_variable_set ("@ value", 1) '(где ': iterator' - это имя последовательности). Обратите внимание, что это работает только в глобальных последовательностях. –

2

Должен был гарантировать, что последовательности идут от 1 до 8 и перезапускаются до 1 и так далее. Реализовано так:

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 

doc говорит: «Значение просто нужно поддерживать метод #next.» Но чтобы сохранить объект CustomSequence, он должен поддерживать метод #peek. Наконец, я не знаю, как долго это будет работать, потому что это как-то взломать внутренности FactoryGirl, когда они вносят изменения, это может не сработать должным образом.

0

Если вы используете Cucumber, вы можете добавить это к определению шага:

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

Тогда просто позвоните по необходимости.

Смежные вопросы