Тестирование динамических начальных состояний с помощью FactoryGirl и StateMachine - PullRequest
5 голосов
/ 20 апреля 2011

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

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

Ответы [ 2 ]

3 голосов
/ 21 апреля 2011

Вы можете просто добавить обратный вызов 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 будет сложно, поскольку состояние еще не будет установлено.

0 голосов
/ 23 сентября 2013

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

Factory.define
  factory :car do
    after(:build) do |car|
      car.initialize_state
    end
  end
end
...