State MAChine, Model Validations and Rspec

State Machine, Model Validations and RSpec

It looks like you're running into a caveat noted in the documentation:

One important caveat here is that, due to a constraint in ActiveModel's validation
framework, custom validators will not work as expected when defined to run
in multiple states. For example:

 class Vehicle
include ActiveModel::Validations

state_machine do
...
state :first_gear, :second_gear do
validate :speed_is_legal
end
end
end

In this case, the :speed_is_legal validation will only get run
for the :second_gear state. To avoid this, you can define your
custom validation like so:

 class Vehicle
include ActiveModel::Validations

state_machine do
...
state :first_gear, :second_gear do
validate {|vehicle| vehicle.speed_is_legal}
end
end
end

RSpec model validation test error in Proc

I ran into an issue with shoulda-matchers when testing uniqueness of and this thread helped. Rspec validates_uniqueness_of test failing with additional validation errors

I'm guessing that the test is checking the subject, which you haven't set, so it's creating a sweep object on its own. This object wouldn't be generated from factory bot and thus that field would be nil instead of [].

Try adding this inside the describe Sweep:

subject { FactoryBot.create(:sweep, simulation: @simulation) }

RSpec skip one of many validations in creating an object

If you want to specifically test validations, you may just build objects without saving them and then test calling #valid? method.
Something like (assuming FactoryGirl for creation)

expect(build(:something, { field: value })).to be_valid

If you want to save objects with validations running, but avoid using S3, as suggested by @apneadiving in the comment, you may change storage setting for test environment. You can do that by modifying the carrierwave initializer like

CarrierWave.configure do |config|
if Rails.env.test?
config.storage = :file
end
end

Testing dynamic initial states with FactoryGirl and StateMachine

You may be able to just add an after_build callback on your factory:

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

However, I don't think you should rely on setting your initial state in this way. It is very common to use ActiveRecord objects like FactoryGirl does (i.e. by calling c = Car.net; c.my_column = 123).

I suggest you allow your initial state to be nil. Then use an active record callback to set the state to to the desired value.

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

I think this will give you more predictable results.

One caveat is that working with unsaved Car objects will be difficult because the state won't be set yet.



Related Topics



Leave a reply



Submit