Reputation: 14434
I have two attributes within my model that share the same validations
validates :first_name, :last_name, length: {minimum: 2}
Right now I have the :first_name
attribute tested as follows:
RSpec.describe User, :type => :model do
it 'is invalid if the first name is less than two characters'
user = User.create(
first_name: 'a'
)
expect(user).to have(1).errors_on(:first_name)
end
For the sake of a developer who isn't familiar with how I've setup my model I wanted to explicitly state the two attributes' relationship with something like this:
it 'is invalid if the first name and/or last name has less than two characters'
user = User.create(
first_name: 'a',
last_name: 'b
)
expect(user).to have(1).errors_on(:first_name, :last_name)
Obviously this throws the error:
wrong number of arguments (2 for 0..1)
The same thing would apply if I had instituted 2 two validations:
validates :first_name, :last_name, length: {minimum: 2}, format: {with: /^([^\d\W]|[-])*$/}
And try to test for 2 errors:
it 'is invalid if the first name and/or last name has less than two characters and has special characters'
user = User.create(
first_name: '@',
last_name: '#
)
expect(user).to have(2).errors_on(:first_name, :last_name)
Upvotes: 0
Views: 895
Reputation: 1490
In RSpec 3.x, you can compound expectations with .and
:
it 'is invalid if the first name and/or last name has less than two characters' do
user = User.create(first_name: 'a', last_name: 'b')
expect(user).to have(1).errors_on(:first_name).and have(1).errors_on(:last_name)
end
Check out the rspec-expectations documentation for more info.
For RSpec 2.x, you'll need to do one of these:
it 'is invalid if the first name and/or last name has less than two characters' do
user = User.create(first_name: 'a', last_name: 'b')
expect(user).to have(1).errors_on(:first_name) && have(1).errors_on(:last_name)
end
# or
it 'is invalid if the first name and/or last name has less than two characters' do
user = User.create(first_name: 'a', last_name: 'b')
expect(user).to have(1).errors_on(:first_name)
expect(user).to have(1).errors_on(:last_name)
end
It's not as pretty, but it should work.
EDIT:
OP was using rspec-collection_matchers
gem. That gem's custom matchers do not include RSpec 3 mixin module RSpec::Matchers::Composable
, so the #and
method goes unrecognized.
There are a few things to do to circumvent this issue. The easiest is to use the &&
technique above (in my RSpec 2.x suggestions). To use only RSpec 3 matchers, you need to use be_valid
:
it 'is invalid if the first name and/or last name has less than two characters' do
user = User.create(first_name: 'a', last_name: 'b')
expect(user).to_not be_valid
end
Of course, this does not distinguish between first_name
errors and last_name
errors as was originally intended. To do that with the be_valid
matcher, you'd have to break the test into two tests:
it 'is invalid if the first name has less than two characters' do
user = User.create(first_name: 'a', last_name: 'abc')
expect(user).to_not be_valid
end
it 'is invalid if the last name has less than two characters' do
user = User.create(first_name: 'abc', last_name: 'a')
expect(user).to_not be_valid
end
Upvotes: 1
Reputation: 15957
Your tests should look like this:
it 'invalid length' do
user = User.new(first_name: '@', last_name: '#')
user.valid?
expect(user.errors.count).to eq 2
expect(user.errors[:first_name]).to include "is too short (minimum is 2 characters)"
expect(user.errors[:last_name]).to include "is too short (minimum is 2 characters)"
end
The user.valid?
call will run the new user against the validations which will populate the errors.
That's a very verbose test to do a unit test - I highly recommend shoulda matchers. You can test the above in just two lines:
it { is_expected.to ensure_length_of(:first_name).is_at_least(2) }
it { is_expected.to ensure_length_of(:last_name).is_at_least(2) }
Upvotes: 0