Rails 4 Model column uniqueness not working - ruby-on-rails

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.

Related

Testing Rails model validations with RSpec, without testing AR itself

Testing Rails model validations with RSpec, without testing AR itself
Lets as setup we have model User:
class User < ActiveRecord::Base
validate :name, presence: true, uniqueness: { case_sensitive: false }, on: :create
validate :password, presence: true, format: { with: /\A[a-zA-z]*\z/ }
end
A see several ways to test this:
it { expect(user).to validate_presence_of(:name).on(:create) }
or
it do
user = User.create(name: '')
expect(user.errors[:name]).to be_present
end
My main question is which of the approaches is better and why? Can suggest me different approach?
Additional questions:
How much should I test? As an example, I can write so many tests for the regex, but it will be hell for maintenance.
How much you think will be full test coverage in this example?
The functionalities of:
Rails being able to validate the presence of an arbitrary value on your model
errors being added to an object for an attribute that is missing when a validation for it is configured
are covered in the tests for Rails itself (specifically, in the ActiveModel tests).
That leaves needing to write the tests for the config that covers the business logic of your app eg validating the presence of the specific name attribute on your specific User class etc. In my opinion, the matchers from the shoulda-matchers gem should have you covered:
RSpec.describe User, type: :model do
subject(:user) { build(:user) } # assuming you're using FactoryGirl
describe 'validations' do
specify 'for name' do
expect(user).to validate_presence_of(:name).on(:create)
# NOTE: saving here is needed to test uniqueness amongst users in
# the database
user.save
expect(user).to validate_uniqueness_of(:name)
end
specify 'for password' do
expect(user).to validate_presence_of(:password)
expect(user).to allow_value('abcd').for(:password)
expect(user).to_not allow_value('1234').for(:password)
end
end
end
I think that unless you have specific custom error messages for your errors that you want to test for (ie you've overridden the default Rails ones), then tests like expect(user.errors[:name]).to be_present can be removed (even if you have custom errors, I still think they're of dubious value since those messages will become locale-dependent if you internationalise your app, so I'd test for the display of some kind of error on the page in a feature spec instead).
I can write so many tests for the regex, but it will be hell for maintenance.
I don't think you can really get around this when testing validations for format, so I'd suggest just write some representative test cases and then add/remove those cases as you discover any issues you may have missed, for example:
# use a `let` or extract out into a test helper method
let(:valid_passwords) do
['abcd', 'ABCD', 'AbCd'] # etc etc
end
describe 'validations' do
specify 'for password' do
valid_passwords.each do |password|
expect(user).to allow_value(password).for(:password)
end
end
end
How much you think will be full test coverage in this example?
I've gotten 100% code coverage from reports like SimpleCov when writing unit specs as described above.
These 2 of them should be used, because:
it { expect(user).to validate_presence_of(:name).on(:create) }
=> You are expecting the validate_presence_of should be run on create, this should be the test case for model
it do
user = User.create(name: '')
expect(user.errors[:name]).to be_present
end
=> You are expecting a side effect when creating user with your input, so this should be the test case for controller
Why you shouldn't remove 1 of them:
Remove the 1st test case: what happens if you do database validation level instead, you expect an active record level validation
Remove the 2nd test case: what happens on controller actually creates a new User, how do you expect the error returning!

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 / shoulda: testing, that a custom validator is called

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

RSpec test won't pass. Validating for uniqueness with domain name

I'm using ruby 1.9.2 and rails 3.2.2.
I have a 'domain' model (domain.rb):
class Domain < ActiveRecord::Base
attr_accessible :url
belongs_to :user
VALID_DOMAIN_REGEX = /^[a-z0-9\-\.]+\.[a-z]{2,}$/i
validates :url, presence:true,
format: { with: VALID_DOMAIN_REGEX },
uniqueness: { case_sensitive: false }
end
And a test asserting that a duplicate domain should not be valid:
require 'spec_helper'
describe Domain do
before do
#domain = FactoryGirl.create(:domain)
end
subject { #domain }
describe "when domain url is already taken" do
before do
domain_with_same_url = #domain.dup
domain_with_same_url.url = #domain.url.upcase
domain_with_same_url.save
end
it { should_not be_valid }
end
end
The test keeps failing:
1) Domain when domain url is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/domain_spec.rb:31:in `block (3 levels) in '
#domain is already created, validated and saved.
The domain_with_same_url is the new record, and it should be invalid. But you are not checking it.
Try
domain_with_same_url = FactoryGirl.create(:domain, :url => #domain.url.upcase)
domain_with_same_url.should_not be_valid
Your two before blocks are run in outer-to inner order. Thus, when running your testsuite, first your #domain object gets created and saved, then the inner before block gets executed. Hiwever, your domain_with_same_url probably gets never actually saved because it's validation fails which probably leats to domain_with_same_url.save to return false.
As a workaround, you could check the validity of domain_with_same_url instead of #domain.
Seems like your subject of your test case is #domain, which is a valid object. Whether use new subject for #domain_with_same_url (don't forget to make it an instance variable), or explicitly say domain_with_same_url.should ... (just like The Who indicated in his answer).
I just had the same problem in Rails 4 Model column uniqueness not working. It provides two other solutions without a second call to FactoryGirl, just FYI. The one I am using is:
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 }
The save is causing some problem, unexpected to me. And, you need to reference an object to should_not_be_valid as a local variable.

Rspec, shoulda, validate_uniqueness_of with scope and wrong error message

I have following Rspec test:
describe Productlimit do
before(:each) do
#productlimit = Factory.create(:productlimit, :user => Factory.create(:user))
end
subject { #productlimit }
...
it { should validate_uniqueness_of(:price_cents).scoped_to(:direction_down, :currency, :market_id, :user_id) }
...
end
But I get following confusing error:
1) Productlimit
Failure/Error: it { should validate_uniqueness_of(:price_cents).scoped_to(:direction_down, :currency, :market_id, :user_id) }
Expected errors to include "has already been taken" when price_cents is set to 9530, got errors: ["direction_down has already been taken (false)"]
Can you help me? I don't understand why this isn't working, because the error message seems to be correct?
EDIT:
This happens too in other situations as well:
# product_spec.rb
...
it { should validate_numericality_of(:price).with_message("price_cents must be greater than 0 (0)") }
# rake spec:models
Failure/Error: it { should validate_numericality_of(:price).with_message("price_cents must be greater than 0 (0)") }
Expected errors to include "price_cents must be greater than 0 (0)" when price is set to "abcd", got errors: ["price_cents must be greater than 0 (0)"]
To check validate_uniqueness_of(:field) but for this you should at lease one record in database to check uniquness constrain.
Here is....
before do
#country = FactoryGirl.create(:country, :id =>"a0000007-0000-0000-0000-000000000009",:currency_id => "a0000006-0000-0000-0000-000000000004",:merchant_id => "a0000001-0000-0000-0000-000000000002",:country_code => 'CAN', :name => 'Canada')
end
To validate uniqueness
it { should validate_uniqueness_of(:country_code)}
It will work out try it out.
To add to what Riche has said about uniqueness validation on :direction_down, I would suspect the way your ProductLimit factory generates values for :direction_down. It might not always be generating unique values for all attributes which have the uniqueness validation on them.
Also, one issue I have faced with uniqueness validation is the first object ( the subject in your case) thats is created before the validation check should not have any conflicting values with ones the factory "randomly" generates. To illustrate with a trivial example,
Factory(:product_limit, :direction_down => "abc")
it { should validate_uniqueness_of(:price_cents).scoped_to(:direction_down) }
has the potential to fail wrongly, in case the object constructed by the shoulda matcher ends with direction_down set to "abc" when there is a uniqueness validation on :direction_down.
Do you have the database_cleaner settings in the sepc_helper?
if not then add
gem "database_cleaner"
in spec_helper.rb
add the following in the RSpec.configure block
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
Also can you post the Factory code? just to have a clearer picture.
I hope this helps.
Also check if there is any uniqueness validation on :direction_down attribute.
Try to remove test/fixtures/* and try to manually create object before testing uniqueness (not using Factory). Also, have you tried:
class MyModelTest < ActiveSupport::TestCase
The error isn't correct for the matcher because it's complaining about the uniqueness of :direction_down when you asked it to test that the uniqueness of :price_cents was valid.
Does the code for your model include a validates_uniqueness_of :direction_down? If so, that would explain the message.
The first case (with validate_uniquess_of) happened to me after an unexpected crash. A simple rake db:test:prepare fixed it.

Resources