Through rspec (I'm using rspec-1.3.0, rspec-rails-1.3.2 gems) generator (ruby script/generate rspec_model suggestion section_id:integer user_id:integer subject:string content:text state:string type:string) I created model and model spec and run rake db:migrate and rake:test:prepare
After that I started to work on my model spec:
require 'spec_helper'
describe Suggestion do
before(:each) do
#valid_attributes = {
:section_id => 1,
:user_id => 1,
:subject => 'Inappropriate title',
:content => 'The title of this section is inappropriate.',
:state => 'new',
:type => 'flag'
}
end
it "should create a new instance given valid attributes" do
Suggestion.create!(#valid_attributes)
end
it "should reject empty section_id attribute" do
empty_section_id_suggestion = Suggestion.new(#valid_attributes.merge(:section_id => ""))
empty_section_id_suggestion.should_not be_valid
end
...
Apart from 1st "should create a new instance given valid attributes" test I created 6 tests, basically each testing attribute of suggestion model for being empty - almost exactly same as "should reject empty section_id attribute" example.
When I run tests I get 6 failed tests, which is fine. First test "should create a new instance given valid attributes" passes.
Now when I go the the suggestion model and add validates_presence_of :all I get following error message related to 1st test:
ActiveRecord::RecordInvalid in 'Suggestion should create a new instance given valid attributes'
Validation failed: All can't be blank
./spec/models/suggestion_spec.rb:16:
When I try to run tests in isolation (validates_presence_of :attribute) all tests are passing, only with :type attribute I get again similar error message:
ActiveRecord::RecordInvalid in 'Suggestion should create a new instance given valid attributes'
Validation failed: Type can't be blank
./spec/models/suggestion_spec.rb:16:
I haven't encountered this problem before (have multiple similar models and their specs passing properly). It looks like it has problem with the :type attribute (it says it can't be empty), even I'm passing value to it through #valid_attributes. I tried to Google search but didn't find similar problem/solution.
Here is the test for :type attribute
it "should reject empty type attribute" do
empty_type_suggestion = Suggestion.new(#valid_attributes.merge(:type => ""))
empty_type_suggestion.should_not be_valid
end
Please check it out and let me know what I'm doing wrong here.
Thanks a lot for help
Peter
in your model you cant just say validate :all because :all isnt a column name.
class Suggestion < AR::Base
validates_pressence_of :subject, :content
end
there would be no reason to validate the presence of id columns but i guess you can if you want.
api documentation:
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_presence_of
So at the end I found the answer for the problem related to :type attribute:
http://www.gyrotechie.com/2008/09/activerecord-does-not-like-attributes-called-type/
The problem was that type is a reserved field name for classes that inherit from ActiveRecord.
I renamed the field name through migration and modified all related files and all is running properly now.
Related
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.
We encountered strange behavior of FactoryGirl. Here is the definition of the commonx_log:
FactoryGirl.define do
factory :commonx_log, :class => 'Commonx::Log' do
log "My log"
resource_id 1
resource "MyString"
last_updated_by_id 1
end
end
Here is the validation in log model:
validates_presence_of :log, :resource, :resource_id
The following rspec would pass:
it "should be OK" do
c = FactoryGirl.build(:commonx_log, :last_updated_by_id => 2)
c.should be_valid
end
However as soon as we are trying to assign value to resource and resource_id:
c = FactoryGirl.build(:commonx_log, :resource => 'resource')
there is an error:
1) Commonx::Log should be OK
←[31mFailure/Error:←[0m ←[31mc.should be_valid←[0m
←[31mexpected #<Commonx::Log id: nil, log: "My log", resource_id: nil, resource: "resource", last_updated_by_id: 1, created_at: nil, updated_at: nil> to be valid
, but got errors: Resource can't be blank←[0m
What could cause the error? Is it resource key work in Factory Girl? Thanks for the help.
UPDATE:
The solution we have is to rename resource to resource_name in log model. After that, we can treat resource_name as regular field and do validation. When resource_id and resource appear in log model, rails assumes that resource is in certain type of association (see post by Ryan Bigg below). This assumption by rails automatically put resource_id and resource in validation and does not allow assigning value to resource_id (resource_id should be from association by default). This assumption causes problem in our app (can not assign resource_id) and we rename resource to break this tie of association.
The issue here is because you're validating the presence of the association. You don't need to do that at all. Remove resource from your validates_presence_of line.
Is there an actual case in your application where log entries can be created without resources? If not, I wouldn't be too concerned with the validations of these attributes. If you're super worried about resource_id being null, then placing a database constraint on resource_id would be the appropriate way to go.
resource is a rails keyword used for routing, perhaps that is what causes you trouble?
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.
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
I've been working through the tutorials at railstutorial.org, and I was a little stumped by the author's code for section -- 6.2.1 Validating presence.
In the user model, the tutorial adds validates :name, :presence => true. Simple enough.
When the author chooses to write the rspec test, he does something that I thought was a little strange.
describe User do
before(:each) do
#attr = { :name => "Example User", :email => "user#example.com" }
end
.
.
.
it "should require a name" do
no_name_user = User.new(#attr.merge(:name => ""))
no_name_user.should_not be_valid
end
end
Why go through the trouble to merge a blank string to #attr when one could get rid of the :each block statement and simply write:
it "should require a name" do
no_name_user = User.new(:name => "", :email => "user#example.com")
no_name_user.should_not be_valid
end
I know that the author uses the #attr variable to validate the presence of the email address as well, which is one indication as to why he used the block statement -- to me it makes more sense to follow the structure of the second block quote. Still, I have a feeling there is something that I'm missing here.
Another explanation that crossed my mind is that it helps to use the #attr structure when there are lots of keys to be entered, as opposed to this rather simplistic case of only name and email.
Anyone have any input?
It's so there's one standard attributes map that can be used across all the tests. When a test requires that a value isn't there, it's removed.
Personally, I wasn't convinced it was worth it as it sort of obfuscates things (as you discovered), but there it is.
The point is to only have code relevant for the test case in the test. The only relevant attribute when testing that the user isn't valid without a name is the name attribute. That test should not have to know anything about the email attribute.
Let's say you add validation for the presence of a new field – you'd have to update every test where you build up a User without that field. With the attr hash at the top, you just pop the new field in there, and all your tests are fine.
Creating objects for testing is a common enough problem that there are many solutions, and plenty of discussion about which way is best. I'd suggest you look into factories. Machinist and FactoryGirl are two alternatives that work great with Rails.