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
Rails 5 - Using Polymorphic Associations - Rendering the Views
Unable to Delete File from Amazon S3 Using Ruby Script
The Program 'Rails' Is Currently Not Installed
How to Mock Super in Ruby Using Rspec
Catching Command-Line Errors Using %X
Ruby, How to Access Local Variables Outside the Do - End Loop
Losing an Attribute When Saving Through an Association W/ Scope (Rails 4.0.0)
Generic Way to Replace an Object in It's Own Method
How to Set Correct Ruby Version in Gem Environment
Accessing a Has_One Associations' Attributes
What's the Best Way to Return an Enumerator::Lazy When Your Class Doesn't Define #Each
Scraping a Site That Requires Login Username and Password on Two Separate Pages
How to Parse Xml Nodes to CSV with Ruby and Nokogiri
Differencebetween Unicorn and Unicorn_Rails
How to Call Methods Defined in Applicationcontroller in Models
Https Request Using Net::Http's Block Form -- Is It Possible
Conditional Page Caching [Solution: Conditional Fragment Caching]