Тестирование динамических начальных состояний с помощью FactoryGirl и StateMachine

У меня проблемы с тестированием StateMachines с 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

Итак, начальное состояние зависит от того, установлен ли атрибут 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'

Однако, поскольку 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

person Gareth    schedule 20.04.2011    source источник


Ответы (2)


Вы можете просто добавить обратный вызов after_build на своей фабрике:

Factory.define :car do |c|
  c.after_build { |car| car.initialize_state }
end

Однако я не думаю, что вам следует полагаться на настройку исходного состояния таким образом. Очень часто используются объекты ActiveRecord, такие как FactoryGirl (т.е. путем вызова 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

Думаю, это даст более предсказуемые результаты.

Одно предостережение заключается в том, что работать с несохраненными объектами Car будет сложно, потому что состояние еще не будет установлено.

person phylae    schedule 21.04.2011

Пробовал ответ phylae, обнаружил, что новый FactoryGirl не принимает этот синтаксис, а метод after_build не существует в объекте ActiveRecord. Этот новый синтаксис должен работать:

Factory.define
  factory :car do
    after(:build) do |car|
      car.initialize_state
    end
  end
end
person denis.peplin    schedule 23.09.2013