I am learning how to write test cases using Rspec. I have a simple Post Comments Scaffold where a Post can have many Comments. I am testing this using Rspec. How should i go about checking for Post :has_many :comments. Should I stub Post.comments method and then check this with by returning a mock object of array of comment objects? Is testing for AR associations really required ?
Since ActiveRecord associations should be well-tested by the Rails test suite (and they are), most people don't feel the need to make sure they work -- it's just assumed that they will.
If you want to make sure that your model is using those associations, that's something different, and you're not wrong for wanting to test that. I like to do this using the shoulda gem. It lets you do neat things like this:
describe Post do
it { should have_many(:comments).dependent(:destroy) }
end
Testing associations is good practice generally, especially in an environment where TDD is highly regarded- other developers will often look to your specs before looking at the corresponding code. Testing associations makes sure that your spec file most accurately reflects your code.
Two ways you can test associations:
With FactoryGirl:
expect { FactoryGirl.create(:post).comments }.to_not raise_error
This is a relatively superficial test that will, with a factory like:
factory :post do
title { "Top 10 Reasons why Antelope are Nosy Creatures" }
end
return you a NoMethodError if your model lacks a has_many association with comments.
You can use the ActiveRecord #reflect_on_association method to take a more in-depth look at your association. For instance, with a more complex association:
class Post
has_many :comments, through: :user_comments, source: :commentary
end
You can take a deeper look into your association:
reflection = Post.reflect_on_association(:comment)
reflection.macro.should eq :has_many
reflection.options[:through].should eq :user_comments
reflection.options[:source].should eq :commentary
and test on whatever options or conditions are relevant.
If you'd rather not use an external gem like shoulda to test your associations (see Robert Speicher's Answer for details on that), another option is to use reflect_on_association to get the AssociationReflection object for the relevant association, and then assert on that:
describe Post do
it "should destroy its comments when it is destroyed" do
association = Post.reflect_on_association(:comments)
expect(association).to_not be_nil
expect(association.options[:dependent]).to eq :destroy
end
end
Most people don't test the associations, as Rails already has unit tests to make sure those methods work correctly. If you are doing something complex, like involving a proc or something, you might want to explicitly test it. Usually you can do this by just doing
a = Post.new
a.comments << Comment.new
assert a.save
assert a.comments.size == 1
or something akin to that.
Related
I'm pretty new to rails testing and I tried using shoulda but it breaks whatever model I put it in so I figured I should just test them manually.
So my question to you oh great experts of SO is this:
How can I use test::unit to test that a model is associated with another model?
well, the most obvious solution would be something like this:
user = User.create(name: 'User')
2.times { user.friends.create(name: 'Other User') }
assert_equal(2, user.friends.count, "Unexpected associated records count")
but this is basically more like testing rails itself, you should not bother with that.
I would just check if user.respond_to?(:friends). But, of course, it will fail if someone defines a method with that name
So, I have a factory that produces a user with a couple of associations. Instead of creating a new factory for each case, I'd like to create the object and modify some attributes of the association objects.
let(:user) do
user = FactoryGirl.create(:user_with_associations)
user.associations.where(some_id: user.default_thing.id).first.some_attribute = Time.now
user.save
user
end
But if I call puts user.associations.to_yaml in an it block, it does not have the updated attribute of some_attribute = Time.now.
What am I missing? Thanks.
A couple of suggestions...
First, you seem to be working against FactoryGirl's strength. A better approach IMHO is to move your customization code into a factory, for example define a FactoryGirl factory for whatever you're building.
Example:
let(:user){ FactoryGirl.create(:user_with_time_now) }
This is really the whole point of FactoryGirl IMHO.
Second, if you're using Rails and ActiveRecord, look at :inverse_of for associations. These tell ActiveRecord that the associations should loop back to the original object.
I have the following model:
class Kueue < ActiveRecord::Base
attr_accessible :name, :user_id
belongs_to :user
has_and_belongs_to_many :photos
scope :empty, includes(:photos).where{ photos.id.eq(nil) }
scope :with_photos, includes(:photos).where{ photos.id.not_eq(nil) }
end
I want to write specs for the scopes, to make sure they're reliably working. My problem is how to deal with the Photo model. Photos have a lot of validations on them, for instance they must belong_to a User. So, I can write a working spec for these queries like so:
describe "queries" do
before :each do
#empty = Fabricate(:kueue)
#full = Kueue.new :name => "Full Queue"
#full.photos << Fabricate(:photo)
#full.save
end
it "should find empty queues" do
Kueue.empty.should_not include(#full)
end
it "should find queues with photos" do
Kueue.with_photos.should_not include(#empty)
end
end
However, as you can imagine this is sloooow. This makes a bunch of calls to the database. (1) to make two kueues, (2) to make a photo, (3) to make a user who owns the photo... and so on, down the line.
This seems like a simple enough problem, all I need is one join record between a photo and a kueue and I can test this really fast. So how would you go about mocking this interaction so you can test it faster and in better isolation?
PS: I'm using Rails 3.2.8, Squeel (hence the query syntax) and my model is called Kueue because Queue is a reserved word in Rails :)
i think that it would not make sense to mock in the context of scopes.
scopes are basically database-queries and you want to make sure they work with, you know, the database.
so if your problem is test-performance, then i would suggest:
try out fixtures in this case
OR
skip model validation, to reduce the models needed
I have a model with polymorphic association that I am running rspecs on.
The tests require the polymorphic field to point to a model with some fields (e.g. name).
Since I prefer not to use any of my existing (and complex)models, I was thinking of somehow creating a new simple models (that will only exist in tests) that my main models can point to.
Is there a way to do this ?
Any other way to test models when they are dependant on another models ?
As you expect, tests should be independant.
This is where stubs and mocks appear and it's the Rails way to proceed.
If you want to create a module dedicated to tests (which is overkill cause it's the role of stubs), just remind ruby classes and modules are executable.
So you could simply do:
if Rails.env.test?
module Foo
...
end
end
Problem solved - using the 'mocha' gem.
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
In tests, can just do:
class MockUser {
# Stuff
}
comment = Comment.create!.expects('commentable').returns(MockUser.new)
Now it will simply work:
comment.commentable.name
I am new to testing Rails web applications and RSpec. I work with legacy code and need to add tests. So what is the best way to test finders and named scopes with RSpec?
I find in Google a few approaches but they are not ideal.
For example:
http://paulsturgess.co.uk/articles/show/93-using-rspec-to-test-a-named_scope-in-ruby-on-rails
it "excludes users that are not active" do
#user = Factory(:user, :active => false)
User.active.should_not include(#user)
end
or
http://h1labs.com/notebook/2008/8/21/testing-named-scope-with-rspec
it "should have a published named scope that returns ..." do
Post.published.proxy_options.should == {:conditions => {:published => true}}
end
I find best approach (IMHO) in "Rail Test Prescriptions":
should_match_find_method :active_only { :active == true }
where should_match_find_method custom helper method
The creator of RSpec has recently blogged he thinks Validations are behavior, associations are structure. In other words he finds that associations (and scopes) should not nessesarily be tested directly. Tests for these will follow from the behavior you want.
In other words, current wisdom is that there is no need to test each scope directly, since you will cover these associations by testing the behavior of your application.
David Chelimsky testing scopes (updated)
David Chelimsky example, (linked by Sam Peacey's comment), modernised.
# app/models/user.rb
class User < ActiveRecord::Base
scope :admins, -> { where(admin: true) }
end
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
describe ".admins" do
it "includes users with admin flag" do
admin = User.create!(admin: true)
expect(User.admins).to include(admin)
end
it "excludes users without admin flag" do
non_admin = User.create(admin: false)
expect(User.admins).not_to include(non_admin)
end
end
end
This produces a more 'spec-like' output (when using --format documentation):
User
.admins
includes users with admin flag
excludes users without admin flag
Note about origination of this answer:
David Chelimsky, the RSpec lead at the time, answered this question and Sam Peacey's link to it has more votes than the actual answer. The answer is not easy to find and follow as he is replying to someone and editing their answer in an email chain. This answer cleans that up and updates the RSpec code as, I guess, he would have written it today.
From https://coderwall.com/p/hc8ofa/testing-rails-model-default_scope-with-rspec
no database queries
no need to represent the query in a structure
Example:
class Trip < ActiveRecord::Base
default_scope { order(departure: :asc) }
...
end
RSpec.describe Trip, type: :model do
it "applies a default scope to collections by departure ascending" do
expect(Trip.all.to_sql).to eq Trip.all.order(departure: :asc).to_sql
end
end
The problem with the first approach is that it actually queries the database. It is slow and unnecessary. If you don't mind, you can safely use the first approach. The second approach is fast and clear so I would recommend it.