How can I reset a factory_girl sequence?

Viewed 14011

Provided that I have a project factory

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

And that I'm creating instances of project

Factory.build :project
Factory.build :project

By this time, the next time I execute Factory.build(:project) I'll receive an instance of Project with a title set to "project 3 title" and so on. Not surprising.

Now say that I wish to reset my counter within this scope. Something like:

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

What would be the best way to achieve this?

I'm currently using the following versions:

factory_girl (1.3.1) factory_girl_rails (1.0)

9 Answers

After tracing my way through the source code, I have finally come up with a solution for this. If you're using factory_girl 1.3.2 (which was the latest release at the time I am writing this), you can add the following code to the top of your factories.rb file:

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

Then, in Cucumber's env.rb, simply add:

After do
  Factory.reset_sequences
end

I'd assume if you run into the same problem in your rspec tests, you could use rspecs after :each method.

At the moment, this approach only takes into consideration sequences defined within a factory, such as:

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

I have not yet written the code to handle: Factory.sequence...

There is a class method called sequence_by_name to fetch a sequence by name, and then you can call rewind and it'll reset to 1.

FactoryBot.sequence_by_name(:order).rewind

Or if you want to reset all.

FactoryBot.rewind_sequences

Here is the link to the file on github

If you are using Cucumber you can add this to a step definition:

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

Then just call it when needed.

Related