Rspec / shoulda: testing, that a custom validator is called - ruby-on-rails

I have a custom validator (located in app/validators/uri_validator.rb) which is used in:
validates :link, uri: true
How do I specify this in my specs?
Ideally I would like to have a one-line call, such as:
it { should validate_uri_of(:link) }
How do I do this?

Another option is to use the allow_value matcher, although not ideal it can work in some circumstances.
it { should allow_value(value_which_is_valid).for(:link) }
it { should_not allow_value(value_which_is_invalid).for(:link) }

Use factory girl to build a model object with invalid data and one with valid data and call the be_valid matcher.
#factory girl
build(:model, link: valid_uri).should be_valid
build(:model, link: invalid_uri).should_not be_valid

Related

rails 4 RSpec test validations

It looks like you used to be able to write RSpec tests with the following syntax
it { should validate_presence_of :privacy }
However I'm receiving the following error
error undefined method `validate_presence_of' for #<RSpec::ExampleGroups::Review:0x007fd819c1bdc8>
I can write tests the following way but the above syntax is much simpler
it "should require privacy" do
expect(FactoryGirl.build(:review, privacy: "")).to_not be_valid
end
Is there a 1 liner to test validations using Rails 4.2 and rspec-rails 3.0? I feel like I'm missing something...
Yes there is:
it { is_expected.not_to be_valid }
You can read all about it at Relish
Edited to add clarification:
This assumes the subject is either explicitly stated, like
subject{ FactoryGirl.build(:wiget) }
or is able to be inferred.

RSpec test for not allowing two users with the same email address

First of all I should probably mention that I'm very new to Rails and this is my first "serious" project, so I apologise if this is a simple question but I can't find an answer for it.
I'm using TDD in my project and am using RSpec to write the model tests, FactoryGirl to create the models and Faker to create dummy data for the models. Everything has been going really well until I added a test to make sure no two users have the same email address. In my User model I validated it like so:
# /app/models/user.rb
validates :email, :password_reset_code, :auth_token, uniqueness: true
My factory creates a user model with Faker, like so:
# /spec/factories/users.rb
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
password { Faker::Internet.password }
password_reset_code { Faker::Lorem.word }
auth_token { Faker::Lorem.word }
end
end
and my user_spec.rb test for this is as follows:
# /spec/models/user_spec.rb
it "is invalid with a duplicate email" do
user = FactoryGirl.create(:user)
FactoryGirl.create(:user, email: user.email).should_not be_valid
end
Here I'm creating a new model with FactoryGirl using its dummy values from Faker, saving it to the database and then creating another one with the same email as the first one. I'd expect RSpec to tell me this test passed because of the should_not be_valid part. But instead I get this output when I run the test:
Failures:
1) User is invalid with a duplicate email
Failure/Error: FactoryGirl.create(:user, email: user.email).should_not be_valid
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/models/user_spec.rb:19:in `block (2 levels) in <top (required)>'
So it seems that the model validation is raising an error which RSpec isn't catching and using to pass the test? I've managed to work around it by changing the test to this:
it "is invalid with a duplicate email" do
begin
user = FactoryGirl.create(:user)
FactoryGirl.create(:user, email: user.email).should_not be_valid
rescue
false
end
end
which seems to work, however I have a feeling this isn't the best way to do it.
What's the proper way to write this test?
I ran into this problem too. The error you're encountering is due to the fact that the create() method actually persists the model, which is then throwing an ActiveRecord::RecordInvalid at the DB layer (well, the persistence layer). If you want to assert if a model is valid or not you should use the build() method for your second object and then ask if it's valid. You can read up on it in this post.
Additionally, if you're just trying to test various validations on models and what not I wrote a quick and dirty gem that you can use to assert some of the more basic model validations. You can check it out here.
Hope that helps.
I would go with:
# /spec/models/user_spec.rb
describe 'validations' do
context 'with a duplicate email' do
let(:other_user) { FactoryGirl.create(:user) }
let(:attributes) { FactoryGirl.attributes_for(:user) }
subject(:user) { User.new(attributes.merge(email: user.email)) }
it 'is not valid' do
expect(user).to_not be_valid
end
end
end

RSpec with let and subject features

There is the following spec:
require 'spec_helper'
describe Place do
let(:place) { FactoryGirl.create(:place) }
subject { place }
it { expect be_valid }
describe 'when content is not present' do
before { place.content = nil }
it { expect be_valid }
end
end
Also there is validation for presence content in Place model. But this spec doesn't throw any exception, even if the last instruction sets 'content' as nil. What's the trouble? Thanks.
You are just setting an expectation but never calling anything.
Replace your expect be_valid calls with
it { expect(subject).to be_valid }
...
it { expect(place).to be_valid }
I guess you are coming from the old should syntax. There you could have written it the way you did:
it { should be_valid }
But the newer expect syntax behaves slightly different
You have let(:place) { FactoryGirl.create(:place) }, so when you reference place for the first time then it creates an instance (with valid content). You are then changing content, but validation is not performed on it.
There are a few ways for doing what you need:
Use the factory that you already have, but instead of create use build
Define an invalid factory at FactoryGirl:
factory :place do
content 'valid value'
factory :invalid_place do
content nil
end
end
3) Or you can use attributes_for

Rails 4 Model column uniqueness not working

This should be simple but I cannot figure out why it doesn't work. I have seen many more complex uniqueness constructs here. I have column that should be a unique index. I have specified it twice in the model, just testing options, but my test for uniqueness continues to fail.
Model validation code:
validates :feed_url, presence:true,
format: {with: VALID_URL_REGEX},
uniqueness: true
validates_uniqueness_of :feed_url
RSpec code:
before do
#feed = FactoryGirl.create(:feed)
end
...
describe "when URL is already taken" do
before do
feed_with_same_url = #feed.dup
feed_with_same_url.feed_url = #feed.feed_url
feed_with_same_url.save
end
it { should_not be_valid }
end
The save should not be valid but the tests fails because the model says it is valid even though the fields are equal. I have checked the fields myself at a breakpoint and they are exactly the same in case, length, value, etc.
Tests for presence and Regex validity work perfectly, so the model is working.
I am trying to do this in the model as opposed to the index. I believe I read last night that Rails 4 prefers (deprecates?) these tests in the code instead of the database, but I cannot find that source tonight. (?) Either way, I'd like to see the model working.
You have to call should_not be_valid on some object. Try this
before(:each) do
#feed_with_same_url = #feed.dup
#feed_with_same_url.feed_url = #feed.feed_url
end
it { #feed_with_same_url.should_not be_valid }
What is it in the context of that test? Without the full code, it's probably not feed_with_same_url, so your test is not checking what you think it's checking.
If I was writing this test, it would be something like:
let(:feed) { FactoryGirl.create :feed }
let(:feed_with_same_url) { feed.dup }
subject { feed_with_same_url }
it { should_not be_valid }
Now it is the subject, which is feed_with_same_url.

Mocking Validates Uniqueness Callback

How do you mock out the validates :name, uniqueness: true callback for an attribute?
I imagine it would work something like this:
describe "with duplicate name" do
before { mock(subject.name).unique? { false } }
it "should be invalid" do
subject.should have(1).error_on(:name)
end
Why not using shoulda matchers? With it you can test standard common rails functionality with one-liners like it { should validate_uniqueness_of(:name) }. I find it more readable and eliminates the need for mocking callbacks and preparing test cases

Resources