Mocking Validates Uniqueness Callback - ruby-on-rails

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

Related

rspec Testing of Canadian postal codes with regex, and what other good tests for rspec for CRUD operations

I am doing some rspec testing for a Canadian postal code. It should pass the test if it follows the regex format. Below is my code
require 'rails_helper'
RSpec.describe Postal, type: :model do
context 'validation tests' do
it 'should match regex form' do
postal=(Postal.new(postalcode: 'V5A1S6'))
expect((postal.postalcode).to match(/\A^(?!.*[DFIOQU])[A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9]$\z/))
end
end
end
However, I am getting this failure message.
1) Postal validation tests should match formatting
Failure/Error: expect((postal.postalcode).to match(/\A^(?!.*[DFIOQU])[A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9]$\z/))
ArgumentError:
bad value for range
# ./spec/models/postal_spec.rb:22:in `block (3 levels) in <main>'
What should I do to get it to pass the regex validation? i'm not sure which part of the code is wrong, or if there is a better solution to test for regex in rspec.
Because in my model validation I have this, and the regex works fine.
validates :postalcode, presence: true, length: {minimum: 6, maximum: 6}, format: { with: /\A^(?!.*[DFIOQU])[A-VXY][0-9][A-Z] ?[0-9][A-Z][0-9]$\z/}
Also I was curious. What other good rspec tests would be meaningful when testing a Canadian postal code, or any other postal codes. All I know of so far is testing the character limits and the regex format.
I assume you are got the Regex working, here how you should check the validation in Rspec
RSpec.describe Postal, type: :model do
context 'canadian postal code' do
let(:postal) { Postal.new(postalcode: 'V5A1S6') } # ideally should use factory like FactoryGirl
it 'record is valid' do
expect(postal).to be_valid
# same as above but more explicit
expect(postal.valid?).to be_truthy
# even more explicit
expect(postal.valid?).to eq true
end
end
context 'invalid postal code' do
let(:postal) { Postal.new(postalcode: '382481') } # ideally should use factory like FactoryGirl
it 'record is invalid' do
expect(postal).to be_invalid
# same as above but more explicit
expect(postal.valid?).to be_falsey
expect(postal.invalid?).to be_truthy
# even more explicit
expect(postal.valid?).to eq false
expect(postal.invalid?).to eq true
end
end
end
I strongly recommend to read following refs.
Ref:
valid? and invalid?
RSpec: Predicate Matchers

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!

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.

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.

Resources