Rails - Test validation of enum fields
Asked Answered
B

6

22

I'm using Rails 4 enums and I want to properly test them, so I set these tests up for my enum fields:

it { should validate_inclusion_of(:category).in_array(%w[sale sale_with_tax fees lease tax_free other payroll]) }
it { should validate_inclusion_of(:type).in_array(%w[receivable payable]) }

And this is the model they're validating:

class Invoice < ActiveRecord::Base
  belongs_to :user

  enum category: [:sale, :sale_with_tax, :fees, :lease, :tax_free, :other, :payroll]
  enum type: [:receivable, :payable]

  validates :user, presence: true
  validates :issue_date, presence: true
  validates :series, presence: true
  validates :folio, presence: true
  validates :issuing_location, presence: true
  validates :payment_method, presence: true
  validates :last_digits, presence: true
  validates :credit_note, presence: true
  validates :total, presence: true
  validates :subtotal, presence: true
  validates :category, presence: true
  validates_inclusion_of :category, in: Invoice.categories.keys
  validates :type, presence: true
  validates_inclusion_of :type, in: Invoice.types.keys
end

But when I run the tests I get:

1) Invoice should ensure inclusion of type in [0, 1]
     Failure/Error: it { should validate_inclusion_of(:type).in_array([0,1]) }
     ArgumentError:
       '123456789' is not a valid type
     # ./spec/models/invoice_spec.rb:20:in `block (2 levels) in <top (required)>'

  2) Invoice should ensure inclusion of category in [0, 1, 2, 3, 4, 5, 6]
     Failure/Error: it { should validate_inclusion_of(:category).in_array([0,1,2,3,4,5,6]) }
     ArgumentError:
       '123456789' is not a valid category
     # ./spec/models/invoice_spec.rb:19:in `block (2 levels) in <top (required)>'

I've also tried with string values in the test arrays, but I get the same error and I really don't understand it.

Brunabrunch answered 21/4, 2015 at 18:17 Comment(2)
This question is a duplicate of https://mcmap.net/q/588576/-rails-4-enum-validation. As Albertis rightly states, "I'm not sure that this validation makes sense, since trying to assign an invalid value to status raises an ArgumentError"Rory
May not be related, but definitely an interesting read: Using Enum as abstraction when testingClupeid
S
25

Using Shoulda matchers we can use the following to test the enum

it { should define_enum_for(:type).with([:receivable, :payable]) }

it { should define_enum_for(:category).
            with([:sale, :sale_with_tax, :fees, :lease, :tax_free, :other, :payroll]) }
Stoss answered 9/11, 2016 at 17:18 Comment(0)
C
2

Try this:

it { should validate_inclusion_of(:category).in_array(%w[sale sale_with_tax fees lease tax_free other payroll].map(&:to_sym)) }

Additionally, for code-cleanup, try putting the valid categories/types in a corresponding constant. Example:

class Invoice < ActiveRecord::Base
  INVOICE_CATEGORIES = [:sale, :sale_with_tax, :fees, :lease, :tax_free, :other, :payroll]
  enum category: INVOICE_CATEGORIES
end
Chronic answered 21/4, 2015 at 18:28 Comment(1)
Same issue with your solution, thanks for the cleanup tips, though.Brunabrunch
G
2

Your migration could be the issue, it should look something like:

t.integer :type, default: 1

You may also consider testing this another way.

Maybe more like:

it "validates the category" do
  expect(invoice with category fee).to be_valid
end
Greta answered 21/4, 2015 at 18:41 Comment(2)
The migration looks the same just defaulting to 0 since the values start from there. And didn't consider it that way, could be enough.Brunabrunch
its best to use the new Rspec syntax (opinion), and its better to test validity rather than directly testing inclusion in cases like these.Greta
H
2

Use shoulda matchers along with check for column_type.

it do
  should define_enum_for(:type).
         with_values([:receivable, :payable]).
         backed_by_column_of_type(:integer)
end

it do 
  should define_enum_for(:category).
         with_values([:sale, :sale_with_tax, :fees, :lease, :tax_free, :other, :payroll]).
         backed_by_column_of_type(:integer)
end
Hardecanute answered 13/8, 2020 at 7:56 Comment(0)
Z
1

Just use shoulda matchers:

it { should define_enum_for(:type).with_values([:receivable, :payable]) }

it { should define_enum_for(:category).with_values(:sale, :sale_with_tax, :fees, :lease, :tax_free, :other, :payroll)}

Zygoma answered 5/9, 2019 at 15:14 Comment(0)
D
0

You have this string in your validations:

validates_inclusion_of :category, in: Invoice.categories.keys

In case of enum

Invoice.categories.keys #=> ["sale", "sale_with_tax", "fees", "lease", "tax_free", "other", "payroll"]

You should update your object data with one of names of your enum.

Decision answered 21/7, 2015 at 8:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.