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.

8vius
  • 5,786
  • 14
  • 74
  • 136
  • 1
    This question is a duplicate of http://stackoverflow.com/questions/25597031/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" – ctc Apr 21 '15 at 18:47
  • May not be related, but definitely an interesting read: [Using Enum as abstraction when testing](http://craftingruby.com/posts/2015/07/07/using-enumerable-as-abstraction-when-testing.html) – onebree Jul 21 '15 at 12:42

6 Answers6

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]) }
Bhavik Parmar
  • 424
  • 1
  • 6
  • 9
shivashankar
  • 1,147
  • 1
  • 10
  • 16
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
Chuck Callebs
  • 16,293
  • 8
  • 56
  • 71
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
Chase
  • 2,748
  • 2
  • 23
  • 32
  • 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. – 8vius Apr 21 '15 at 18:43
  • its best to use the new Rspec syntax (opinion), and its better to test validity rather than directly testing inclusion in cases like these. – Chase Apr 21 '15 at 20:19
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
Bhavik Parmar
  • 424
  • 1
  • 6
  • 9
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)}

Matheus Porto
  • 169
  • 1
  • 5
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.

trinaldi
  • 2,872
  • 2
  • 32
  • 37