31

前提是我有项目工厂

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 标题”的 Project 实例,依此类推。不奇怪。

现在说我希望在这个范围内重置我的计数器。就像是:

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)

4

9 回答 9

44

只需在之前/之后的回调中调用 FactoryGirl.reload。这在 FactoryGirl 代码库中定义为:

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

由于某种原因,调用 FactoryGirl.sequences.clear 是不够的。完全重新加载可能会有一些开销,但是当我尝试使用/不使用回调时,我的测试需要大约 30 秒才能运行。因此,开销不足以影响我的工作流程。

于 2011-12-09T17:34:15.500 回答
12

在通过源代码跟踪我的方式之后,我终于想出了一个解决方案。如果您使用的是 factory_girl 1.3.2(这是我撰写本文时的最新版本),您可以将以下代码添加到您的 factory.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

然后,在 Cucumber 的 env.rb 中,简单地添加:

After do
  Factory.reset_sequences
end

我假设如果您在 rspec 测试中遇到同样的问题,您可以在 :each 方法之后使用 rspecs。

目前,这种方法只考虑工厂内定义的序列,例如:

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

我还没有写出要处理的代码:Factory.sequence...

于 2011-01-07T06:32:15.313 回答
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

于 2014-02-03T10:12:56.230 回答
9

调用了一个类方法sequence_by_name来按名称获取序列,然后您可以调用rewind它并将其重置为 1。

FactoryBot.sequence_by_name(:order).rewind

或者,如果您想全部重置。

FactoryBot.rewind_sequences

这是github上文件的链接

于 2019-01-04T02:10:00.137 回答
5

根据 ThoughBot Here,需要重置测试之间的序列是一种反模式。

总结

如果你有这样的事情:

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 "My Category"
   And I am signed in as an admin
   When I go to create a new post
   And I select "My Category" from "Categories"
   And I press "Create"
   And I go to view all posts
   Then I should see a post with the category "My Category"

不是这个:

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"
于 2011-11-02T05:52:00.333 回答
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

文档说“该值只需要支持该#next方法。” 但是为了让您的 CustomSequence 对象通过它也需要支持#peek方法。最后我不知道这会工作多久,因为它有点侵入 FactoryGirl 内部,当他们做出改变时,这可能无法正常工作

于 2014-11-03T03:31:46.220 回答
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/

于 2010-07-29T16:31:21.253 回答
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
于 2014-07-02T10:28:02.907 回答
0

如果您使用的是 Cucumber,您可以将其添加到步骤定义中:

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

然后在需要时调用它。

于 2017-09-26T20:01:22.807 回答