Enums Testing
Testing Enums on Models
rails
minitest
postgresql

Testing enums on Models is tricky as I found out.
I was building a cart system for my new project and I decided to add a enums for status as follow:
class Cart < ApplicationRecord belongs_to :user has_many :cart_items, dependent: :destroy enum :status, { active: "active", completed: "completed", cancelled: "cancelled" } validates :status, presence: true, inclusion: { in: statuses.keys } def total_price cart_items.sum { |item| item.product.price * item.quantity } end def total_items cart_items.sum(:quantity) end end
And written simple test for invalid status as follow:
test "should not allow invalid status" do cart = Cart.new(status: "invalid_status") assert_not cart.save, "Cart should not be saved with an invalid status" assert_includes cart.errors[:status], "is not included in the list", "Status validation should enforce valid statuses" end
Unfortunately this was throwing ArgumentError.
Error:
CartTest#test_should_not_allow_invalid_status:
ArgumentError: 'invalid_status' is not a valid status
test/models/cart_test.rb:38:in `block in <class:CartTest>'
This is because rails raises an ArgumentError immediately if an invalid value is assigned for enums, preventing the test from reaching the validation step.
After some research I found a hack to override the setter to catches the error and writes the value directly to the attribute as follow:
def status=(value) super rescue ArgumentError @attributes.write_cast_value("status", value) end
This will add an error instead of crashing with ArgumentError allowing validation to occur. Now this was the approach prior to Rails 7.1 but now with 7.1+ we can simply validate on enum it self.
enum :status, { active: "active", completed: "completed", cancelled: "cancelled" }, validate: true
Its simple as that and just works out of the box. You could also just validate on column it self:
validates_inclusion_of :status, in: [:active, :completed, :cancelled]
But keep in mind if you do this you wont be able to use suffixs or use helper methods like Cart.is_cart_active? no scopes like Cart.active and no type casting.