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.
Related
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
Could someone please explain why Factories are more useful than a simple instantiation during test? More clearly, I do not see the difference between:
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
and this
before(:each) do
#user = Factory(:user)
end
which has the following Factory:
Factory.define :user do |user|
user.name "Michael Hartl"
user.email "mhartl#example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
The bigger your app gets, the more benefits you gain from factories.
Your solution is great for 2-3 models. But lets say you have an article model where you need valid users to test stuff. Now you have 2 files where you define #attr for users. Now imagine there are even more models that need users, like comments, roles, etc. It gets messy.
It is more convenient to use factories. The benefits are you can define multiple default prototypes. Like an admin user, a normal user, an unregistered user etc.
Furthermore the code is DRY, so if you add a new mandatory field you can add it once to your factory and you are done.
So the answer is: Basically they are the same, but the bigger your app gets the more you need a way to manage all your prototypes.
Because it lets you have in a single place all your required variables plus associations.
Moreover, you can easily create stubs or simply extract attributes without additional code.
The interest is clearer once you have several test files as you want to keep your code DRY.
Sidenote:
You should use 'let' instead of creating each time an instance variable
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.
Everybody knows that automated testing is a good thing.
Not everybody knows exacly what to test.
My question is if native validations like validate_presence_of, validate_uniqueness_of and so on should be tested in the application.
In my office we are three, one thinks it should be tested, one thinks it shouldn´t and I am up in the air.
Yes.
Testing that a model attribute is present or not is only testing the validates_presence_of code as a by-product of the real test which is that the validates_presence_of exists within your model.
If someone commented out a bunch of validation code and then forgot to uncomment it then this would go undetected and could cause all sorts of problems.
I test them, not because I think they don't work but to ensure that they are present in my model when required.
Are you planning to write unit tests for every single Ruby operator and API method as well?
Your unit tests should test your own code, not other people's - that's their job, why duplicate their work? And if you don't trust them to do their job well, why are you using their code?
Matthew Bass has a great gem he's released for just this type of thing. It adds rspec matchers that check to make sure the validation is in place without actually running the underlying ActiveRecord code. Read more about it here.
It adds matchers for validations:
it_should_validate_presence_of :first_name, :last_name, :email
it_should_validate_numericality_of :zip
it_should_validate_uniqueness_of :email
it_should_validate_inclusion_of :gender, :in => %w(Male Female)
Also matchers for associations:
it_should_belong_to :employer
it_should_have_many :friends, :romans, :countrymen
it_should_have_one :account
it_should_have_and_belong_to_many :comments
And a few other useful additions:
# tests that User.count increases by 1
it_should_be_createable :with => {:first_name => 'Ed', :last_name => 'The Duck', :email => 'a#b.com'}
# tests that the attribute is protected
it_should_protect :email
That's not by any means an exhaustive list. I've got a fork where I've added a few others I needed, likely there are others floating around as well. It's a good approach and for me fit the middle ground between ensuring the validations were still in the model, and having to explicitly write tests to execute ActiveRecord code to ensure it.
This is where tools like Shoulda really come in handy. I think its totally up to you to test how you write the code with the tools people provide you. Just because you are using has_many, does not mean you are using it right!
Seriously, if you integrate Shoulda into the mix, it becomes trivial to test these sorts of things. if you run rcov, its going to tell you all the code you wrote is not fully tested, unless you do.
Test the code you write. ActiveRecord has great test coverage including coverage for the validation class methods.
Spec:
require 'spec_helper'
describe User do
before(:each) do
#user = User.new
end
it "should not be valid without an email" do
#user.save.should be_false
#user.should_not be_valid
#user.email = "example#example.com"
#user.should be_valid
#user.save.should be_true
end
end
To get that spec to pass you would need
class User < ActiveRecord::Base
validates_presence_of :email
end