Testing uniqueness validation - ruby-on-rails

I'm trying to learn rspec and have ran into a problem, I'm trying to test the uniqueness validation on one of my models, but the test keeps failing even though I'm pretty sure it should pass.
Here is my test:
context "two products with the same title" do
Given{FactoryGirl.build(:product, title: "Hello test title")}
Given(:post2){FactoryGirl.build(:product, title: "Hello test title")}
Then{post2.invalid?}
end
and here is my validator:
validates :title, uniqueness: true
however when I run the test it comes back failed and I'm not sure why?
any help would be great!

You need to add uniqueness validation on title:
validates :title, uniqueness: true
And also you have to create first product not just build it
context "two products with the same title" do
Given{FactoryGirl.create(:product, title: "Hello test title")}
Given(:post2){FactoryGirl.build(:product, title: "Hello test title")}
Then{post2.invalid?}
end
This will create one product with title = "Hello test title"
And for second product with same title product will be invalid

You should use a gem like shoulda-matchers for testing this kind of tests: https://github.com/thoughtbot/shoulda-matchers
It will save you a lot of time and it will DRY your tests (since they are all the same)
Regarding your test, I'm not sure what are you trying to achieve. You are not validating uniqueness there, only length of the product. For adding the uniqueness you add to your Product model:
validates :title, uniqueness: true
And when making the tests, you should create (instead of build) your first Product. Basically, unless your Product is stored in the database, your products will be valid because it doesn't exists (yet) any other Product like that one.

Related

TDD in Rails: clarification on Red - Green testing

Following Michael Hartl's Rails tutorial, I'm unsure how the following validation test is supposed to work:
test "name should be present" do
#user.name = " "
assert_not #user.valid?
end
When this test is written, the testing suite should be Red. After adding the corresponding part in the User class as such:
class User < ActiveRecord::Base
validates :name, presence: true
end
The test becomes Green. I don't understand how the former part works. Is the test Red because the validates part is not implemented yet? After implementing it, #user.valid? should be False turning into a True due to assert_not. Thus, the test is Green?
You have the correct understanding of it. Before a model in Rails is saved to the database, it must be validated. The test here is checking whether the user model would be considered valid and saved with a blank name. As such, before you add validates :name, presence: true, #user.valid? evaluates to true and the test fails. Once you add the validation, the model is considered invalid.
In a Test driven development (TDD) approach we write test before writing codes. Thus, we make the test fail first and then write some codes to make the test pass.
Looks like you do have a correct understanding of whats going on.
assert_not #user.valid? is saying that the #user object is invalid. To start with #user.name is set to a blank value; so we expect it the assertion to pass. However, the code is not yet checking for a valid name to be present. So it fails.
Adding validates :name, presence: true makes the model check for a valid name to be present. Thus the test passes.

rspec mongoid validation of uniqueness

I would like to test the uniquness of User model.
My User model class looks like:
class User
include Mongoid::Document
field :email, type: String
embeds_one :details
validates :email,
presence: true,
uniqueness: true,
format: {
with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z0-9]{2,})\Z/i,
on: :create
},
length: { in: 6..50 }
end
My rspec test which belongs to the model looks like:
...
before(:each) do
FactoryGirl.create(:user, email: taken_mail)
end
it "with an already used email" do
expect(FactoryGirl.create(:user, email: taken_mail)).to_not be_valid
end
After I executed bundle exec rspec it always raises the next error instead of passed with success:
Failure/Error: expect(FactoryGirl.create(:user, email: taken_mail)).to_not be_valid
Mongoid::Errors::Validations:
Problem:
Validation of User failed.
Summary:
The following errors were found: Email is already taken
Resolution:
Try persisting the document with valid data or remove the validations.
If I use this it passes with success:
it { should validate_uniqueness_of(:email) }
I would like to use expect(...). Can anybody help me out?
The issue is you are trying to persist an invalid object into the database, which throws an exception and breaks the test (because email is not unique), before even the test is done using the expect method.
The correct way is to use build here instead of create, which doesn't persist the object in the database, by building the record only in memory and allowing your test to do its job. Therefore to fix it:
expect(FactoryGirl.build(:user, email: taken_mail)).to_not be_valid
Also note that is better to use build rather than create if you don't need to actually save the record in the database, since it's a cheaper operation and you will get the same outcome, unless for some reason your record must be saved to the database for your tests to work in a way you want them, such as saving the first record in in your example.

Validating presence with rspec

This is my first time testing in rails and I'm having trouble with what I think should be a pretty simple validation.
In group_spec.rb
it { should validate_presence_of(:enc_key) }
it { should validate_uniqueness_of(:enc_key).case_insensitive }
In my model
validates :enc_key, :presence => true, uniqueness: {:case_sensitive => false}
When I run rspec I'm getting
2) Group should require enc_key to be set
Failure/Error: it { should validate_presence_of(:enc_key) }
Expected errors to include "can't be blank" when enc_key is set to nil, got errors: ["owner There is no owner associated with this group. (nil)", "name can't be blank (nil)", "name is
too short (minimum is 4 characters) (nil)", "stripped_name can't be blank (nil)"]
The list of errors are generated from other validations and I've tried writing a custom validation but that didn't work either.
I'm guessing you are using shoulda matchers here and I can't see anything wrong with your test.
I think you should be using the validates method rather than the validate method in you model. validates is used to declare the kind of validation rules that you are specifying here. validate is used to declare custom validations.
So try this in your model:
validates :enc_key, presence: true, uniqueness: {case_sensitive: false}
See http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates
and http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate
The shoulda matchers are designed to work with otherwise valid records. They change various aspects of the record and test that the validation reports the appropriate error. If there are already errors in the record, this approach generally doesn't work.
You haven't shown the rest of your spec, but I gather than subject is nil. You need to set subject to be a valid instance of your record.
See https://github.com/thoughtbot/shoulda-matchers/issues/365 for a related discussion of this.

testing field type a model in mongoid

i use ruby on rails. also i use minitest framework for testing and mongoid for database. i want to write a model test. my model is below:
class Identity
include Mongoid::Document
include OmniAuth::Identity::Models::Mongoid
field :name
field :email
field :password_digest
validates :name, uniqueness: true
end
the model test is:
describe Identity do
it "must include OmniAuth::Identity::Models::Mongoid" do
Identity.must_include OmniAuth::Identity::Models::Mongoid
end
it "should have name" do
Identity.new.must_respond_to :name
end
it "should have email" do
Identity.new.must_respond_to :email
end
it "should have password_digest" do
Identity.new.must_respond_to :password_digest
end
it "should type of String" do
Identity.new.name.type.must_equal "String"
end
end
my problem is about testing the field's type
it "should type of String" do
Identity.new.name.type.must_equal "String"
end
How can i test a field's type? Thanks in advance.
Just a tip - while not an answer to your actual question, it is usually not that
wise test implementation details of code, because it is more likely
to change, and if you think of system validation through tests, it is not that important
how it is implemented, but what it does, eg. how it behaves.
A canonical example is that of testing functionality of a Stack class. Instead of
pushing and popping items out of stack and checking the size, it is likely better to
just push and pop things, and see that if you pop an empty stack, you get appropriate
exception. And naturally you want to check that items are returned in last in, first out
(LIFO) order.
So, in your case, instead of testing what kind of type your field name is, rather
test what you do with the name.

What "validation logic" should I test?

I am using Ruby on Rails 3.0.9 and RSpec 2. I would like to know what "validation logic" I should test. That is, if in my model I have:
class User < ActiveRecord::Base
validates :firstname
:presence => true
end
What should I test of the following?
"should have a valid first name"
"should not have a valid first name"
Or should I test both?
You can test both by simply doing this:
it "should validate presence of" do
should validate_presence_of :firstname
end
Take a look at the shoulda matchers for all such standard Rails Validation.
I think you should not test both. This will be enough:
describe User do
it { should validate_presence_of(:firstname) }
end
There is basically no algorithm for validating names, because the form of names is incredibly culture-centric. So, really you should avoid complex validations for something like a person's name. Some places/cultures don't have last names, for example, so even validating their presence isn't proper. There's a whole list of other examples that make validating names a really bad idea. For more information on the issue of validating names itself, see my answer to this question.
That being said, in general, when validating any field, I test both valid and invalid data. I make sure that, when I set a field to a valid value, that the .valid? method returns true, and when it's invalid, that it returns false.
Typically you don't need to do a long list, you just need to test
A typical valid and invalid example
A few edge cases
you can also test for specific values:
describe User do
context "validations" do
it { should_not allow_value("admin").for(:firstname) }
it { should allow_value("JohnDoe").for(:firstname) }
end
end

Resources