Having trouble testing Rails Model validations with shoulda-matchers and factory-girl-rails?

1.1k Views Asked by At

Model:

validates :email,
uniqueness: {
  message: "has been taken."
},
presence: {
  message: "cannot be blank."
},
length: {
  minimum: 3,
  message: "is too short, must be a minimum of 3 characters.",
  allow_blank: true
},
format: {
  with: /\A[A-Z0-9_\.&%\+\-']+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,13})\z/i,
  message: "is invalid.",
  if: Proc.new { |u| u.email && u.email.length >= 3 }
}

RSpec:

before(:each) do
  @user = FactoryGirl.build(:user)
end

it { should validate_length_of(:email).is_at_least(3) }

Error:

Failure/Error: should validate_length_of(:email).is_at_least(3)
   Expected errors to include "is too short (minimum is 3 characters)" when email is set to "xx",
   got errors:
   * "is too short (minimum is 4 characters)" (attribute: password, value: nil)
   * "is too short, must be a minimum of 3 characters." (attribute: email, value: "xx")
   * "is not included in the list" (attribute: state, value: "passive")

Factory:

factory :user, class: User do
  email FFaker::Internet.email
  password FFaker::Internet.password
  username FFaker::Internet.user_name

end

I am using factory_girl_rails with shoulda_matchers. Every time I try to validate my email, I keep getting an error like above. It says the email value is "xx", but the length of the factory email is greater than that. How can I write an rspec that will pass?

2

There are 2 best solutions below

2
arieljuod On

You are understanding the error (and it's cause) wrong. The error says that it expects "is too short (minimum is 3 characters)" but in the errors it can't find that string (rspec finds "is too short, must be a minimum of 3 characters." which is what you defined on your validation).

When you use shoulda matchers and say

it { should validate_length_of(:email).is_at_least(3) }

I'm guessing it creates a test with an email shorter than 3 and checks if it fails, that's why it's ignoring your factory, internally should matcher is setting a fixed length string just to make that test pass.

When you see the errors in user, that test should actually work since the error is actually there, only the string is different. So, you have two options: remove the custom message when length is minimum: 3; or tell the matcher what message you are expecting:

it { should validate_length_of(:email).is_at_least(3).with_message("is too short, must be a minimum of 3 characters.") }
0
max On

Shoulda-matchers tests validations by testing the messages in the errors object.

The valdation matchers only work with the rails default error messages unless you specify otherwise:

it { should validate_length_of(:email).with_message("is too short, must be a minimum of 3 characters.") }

The with_message method is detailed in this comment.