使用FactoryGirl和StateMachine测试动态初始状态

Gar*_*eth 5 testing activerecord ruby-on-rails state-machine factory-bot

我有一些问题,测试StateMachine以s Factory Girl.它看起来像是Factory Girl初始化对象的方式.

我错过了什么,或者这不是应该的那么容易吗?

class Car < ActiveRecord::Base
  attr_accessor :stolen # This would be an ActiveRecord attribute

  state_machine :initial => lambda { |object| object.stolen ? :moving : :parked } do
    state :parked, :moving
  end
end

Factory.define :car do |f|
end
Run Code Online (Sandbox Code Playgroud)

因此,初始状态取决于stolen在初始化期间是否设置了属性.这似乎工作正常,因为ActiveRecord将属性设置为其初始化程序的一部分:

Car.new(:stolen => true)

## Broadly equivalent to
car = Car.new do |c|
  c.attributes = {:stolen => true}
end
car.initialize_state # StateMachine calls this at the end of the main initializer
assert_equal car.state, 'moving'
Run Code Online (Sandbox Code Playgroud)

但是因为Factory Girl在单独设置其覆盖之前初始化对象(请参阅factory_girl/proxy/build.rb),这意味着流程更像是:

Factory(:car, :stolen => true)

## Broadly equivalent to
car = Car.new
car.initialize_state # StateMachine calls this at the end of the main initializer
car.stolen = true
assert_equal car.state, 'moving' # Fails, because the car wasn't 'stolen' when the state was initialized
Run Code Online (Sandbox Code Playgroud)

phy*_*lae 3

您也许可以在工厂中添加 after_build 回调:

Factory.define :car do |c|
  c.after_build { |car| car.initialize_state }
end
Run Code Online (Sandbox Code Playgroud)

但是,我认为您不应该依赖以这种方式设置初始状态。像 FactoryGirl 一样使用 ActiveRecord 对象是很常见的(即通过调用 c = Car.net; c.my_column = 123)。

我建议你让你的初始状态为零。然后使用活动记录回调将状态设置为所需的值。

class Car < ActiveRecord::Base
  attr_accessor :stolen # This would be an ActiveRecord attribute

  state_machine do
    state :parked, :moving
  end

  before_validation :set_initial_state, :on => :create

  validates :state, :presence => true

  private
  def set_initial_state
    self.state ||= stolen ? :moving : :parked
  end
end
Run Code Online (Sandbox Code Playgroud)

我认为这会给你带来更可预测的结果。

需要注意的是,处理未保存的 Car 对象会很困难,因为状态尚未设置。