Rails 3 - Mock/Stub - Testing Controller - ruby-on-rails

i have a question about testing following line of Rails code:
https://gist.github.com/1289849
in my test code i have something like this(obviously don't works):
https://gist.github.com/1289848
Someone can help me write right test code for this ?
Thanks

To make testing it easier, you should move this logic to a class method on your Client model. I'm also assuming you have a has_many :clients on your user model, which is what your query is implying.
Something like:
class Client
def self.search_by_name(name)
all.where("name LIKE %?%", name).order("name ASC")
end
end
Then in your controller:
#clients = current_user.clients.search_by_name(params[:search])
This will allow you to test in a unit test, rather than with an integration test.
client_spec.rb:
describe Client, 'searching by name' do
let(:current_user) { User.create!(...) }
let!(:client) { Client.create!(:name => 'client name', :user => current_user) }
it 'should find the clients by name' do
Client.search_by_name('client name').should include(client)
end
end
Then your integration test could just stub the search_by_name method and return a collection of mocks, making it easier to test.

Related

How to write a Klass.any_instance_with_id(id).expects(:method) in RSpec

When writing tests using RSpec, I regularly have the need to express something like
Klass.any_instance_with_id(id).expects(:method)
Main reason is that in my test, I often have the object that should receive that method call available, but due to the fact that ActiveRecord, when loading the object with that id from the database, will create a different instance, I can't put the "expects" on my own instance
Sometimes I can stub the find method to force ActiveRecord to load my instance, sometimes I can stub other methods, but having that "any_instance_with_id" would make life so much easier...
Can't image I'm the first having this problem... So if any of you found a "workaround", I'd be glad to find out!
Example illustrating the need:
controller spec:
describe 'an authorized email' do
let(:lead) { create(:lead, status: 'approved') }
it "should invoice its organisation in case the organisation exceeds its credit limit" do
lead.organisation.expects :invoice_leads
get :email
end
end
controller:
def email
leads = Lead.approved
leads.each do |lead|
lead.organisation.invoice_leads if lead.organisation.credit_limit_exceeded?
end
redirect_to root_path
end
It seems weird to me you need that for specs.
You should take the problem one level higher: when your app tries to retrieve the record.
Example:
#code
#user = User.find(session[:user_id])
# spec
let(:fake_user) { mock_model 'User', method: false }
it 'description' do
User.should_receive(:find).and_return fake_user
fake_user.expects(:method)
#...
end
Order/invoice example:
let(:order) { mock_model 'order', invoice: invoice }
let(:invoice) { mock_model 'Invoice', 'archive!' => false }

RSpec: how to test that parameters are correct in includes().where()

I am new in Rspec testing of Ruby on Rails and I have a question concerning controller tests.
Please, suggest me how to test this line:
#dreams = Dream.public_dreams.includes(:user).where("users.type_id = ?", 5)
In other words, I want to test if correct parameters were set in the controller. It should display all the dreams of a users with type_id equal to 5.
Can someone, please, help me out?
Since you've indicated that you "want to make sure...displayed dreams [are] only of users who have type_id equal to 5", this would seem to me more like a model spec than a controller spec, and I would probably refactor the code and spec it out to look something like this (assuming you still want to keep your rigid conditions):
First, refactor query into a scope in the model:
class DreamsController < ApplicationController
def your_action
#dreams = Dream.public_dreams_for_type_five_users
end
end
class Dream < ActiveRecord::Base
def self.public_dreams
# your code here to access public dreams
end
def self.public_dreams_for_type_five_users
public_dreams.includes(:user).where("users.type_id = ?", 5)
end
end
Next, test the scope in a model spec against some entries in the database that will pass and fail your expectations (the following spec uses FactoryGirl syntax, but you can substitute it out for whatever fixture-substitute library you like):
require 'spec_helper'
describe Dream do
describe ".public_dreams_for_type_five_users" do
let(:type_five_user_public_dreams) { Dream.public_dreams_for_type_five_users }
context "for users where type_id is 5" do
let!(:type_five_dream) { create(:dream, user: create(:user, type_id: 5)) }
it "includes the user's public dreams" do
expect(type_five_user_public_dreams).to include(type_five_dream)
end
end
context "for users where type_id is not 5" do
let!(:type_six_dream) { create(:dream, user: create(:user, type_id: 6)) }
it "does not include the user's public dreams" do
expect(type_five_user_public_dreams).to_not include(type_six_dream)
end
end
end
end
If you wanted, you could then go and further generalise the class method to be something like Dream.public_dreams_for_users_of_type(id) and change the specs accordingly.
There are several answers to that:
You could test the query itself:I would put such a query in a method or scope of your Dream model.Then go and test the query in a model spec.
You could test the assignment:On the other hand you can test the correct assignment in a controller spec with assigns[:dreams]
#dreams.select { |dream| dream.user.type_id = 5 }.should eq(#dreams) ?

Stubbing model attributes in controller tests

I am finding it very hard to stub certain attributes of a model on a controller test. I want to make sure to stub as little as possible.
EDIT: I have been demoved of using stubs for such integration. I understood that the stubs won't reach the action call. The correct question would now be:
How can one use mocks and stubs to simulate a certain state in a Rails controller test?
So I've reached something like the following:
Spec
require 'spec_helper'
describe TeamsController do
let(:team) { FactoryGirl.create :team }
context "having questions" do
let(:competition) { FactoryGirl.create :competition }
it "allows a team to enter a competition" do
post(:enter_competition, id: team.id, competition_id: competition.id)
assigns(:enroll).team.should == team
assigns(:enroll).competition.should == competition
end
end
# ...
end
Factories
FactoryGirl.define do
factory :team do
name "Ruby team"
end
factory :competition, class: Competition do
name "Competition with questions"
after_create do |competition|
competition.
stub(:questions).
and_return([
"something"
])
end
end
factory :empty_competition, class: Competition do
name "Competition without questions"
questions []
after_create do |competition|
competition.stub(:questions).and_return []
end
end
end
Production code
class TeamsController < ApplicationController
def enter_competition
#team = Team.find params[:id]
#competition = Competition.find params[:competition_id]
#enroll = #team.enter_competition #competition
render :nothing => true
end
end
class Team < ActiveRecord::Base
def enter_competition competition
raise Competition::Closed if competition.questions.empty?
enroll = Enroll.new team: self, competition: competition
enroll.save
enroll
end
end
When I run the test, the questions attribute comes as being nil and so the test fails in the model when checking for nil.empty?.
Why isn't the stub being used so that the state of that message is correctly used? I expected that #competition.questions would be [ "question" ] but instead I get nil.
The problem you're running into is that stub works on an instance of a Ruby object; it doesn't affect all ActiveRecord objects that represent the same row.
The quickest way to fix your test would be to add this to your test, before the post:
Competition.stub(:find).and_return(competition)
The reason that's necessary is that Competition.find will return a fresh Competition object that doesn't have questions stubbed out, even though it represents the same database row. Stubbing find as well means that it will return the same instance of Competition, which means the controller will see the stubbed questions.
I'd advise against having that stub in your factory, though, because it won't be obvious what's stubbed as a developer using the factory, and because it means you'll never be able to test the real questions method, which you'll want to do in the Competition unit test as well as any integration tests.
Long story short: if you stub out a method on an instance of your model, you also need to stub out find for that model (or whatever class method you're using to find it), but it's not a good idea to have such stubs in a factory definition.
When you call create on FactoryGirl, it creates database records which you then retrieve back in your controller code. So the instances you get (#team, #competition) are pure ActiveRecord, without any methods stubbed out.
Personally I would write you test like this (not touching database at all):
let(:team) { mock_model(Team) }
let(:competition) { mock_model(Competition) }
before do
Team.stub(:find) { team }
Competition.stub(:find) { competition }
end
and then in your test something like this:
it "should call enter_competition on #team with #competition" do
team.should_receive(:enter_competition).with(competition)
post :enter_competition, id: 7, competition_id: 10
I don't really understand what your controller is supposed to do or what are you testing for that matter, sorry :(

RSpec stubs & mocks on related (belongs_to) relationships

I'm trying to get my head around RSpec's incredibly confusing, at least initially, syntax by trying to expand on the default specs that are generated with Rails 3 scaffolding...
I have associated models...very simply:
#clown.rb
class Clown < ActiveRecord::Base
has_many :rabbits
end
#rabbit.rb
class Rabbit < ActiveRecord::Base
belongs_to :clown
end
but I'm having trouble with rabbits_controller.spec.rb. In that the specs fail when I reference, say, clown.name in one of rabbit's views. the stupid simple app works as expected but the specs fail because I haven't stubbed (or mocked?) the clown to respond correctly from the rabbit (or at least that's what I think is happening)?!? How should I be adding a stub (or mocking the clown that the rabbit is associate to?).
existing:
#rabbits.controller.spec.rb
require 'spec_helper'
describe RabbitsController do
def mock_rabbit(stubs={})
(#mock_rabbit ||= mock_model(Rabbit).as_null_object).tap do |rabbit|
rabbit.stub(stubs) unless stubs.empty?
end
end
describe "GET index" do
it "assigns all rabbits as #rabbits" do
Rabbit.stub(:all) { [mock_rabbit] }
get :index
assigns(:rabbits).should eq([mock_rabbit])
end
end
...
IMHO (and there are other points of view) this isn't a mocking or stubbing situation. Mocks and stubs are great for external dependencies (think web service), but this is internal to your application. What you want is something like factory_girl, which will let you create test data without the headaches of something like fixtures or trying to mock out every dependent relationship, which quickly becomes monotonous. factory_girl has great documentation, but briefly here's what your factories might look like:
Factory.define(:clown) do |f|
f.rabbits{|c| [c.assocation(:rabbit)]}
f.name "Pierrot"
end
Factory.define(:rabbit) do |f|
f.association :clown
end
You'd then use them in your test like so:
describe RabbitsController do
describe "GET index" do
it "assigns rabbits" do
#rabbit = Factory(:rabbit)
get :index
assigns[:rabbits].should == [#rabbit]
end
end
end

How to spec a scoped new object in the controller

How should I spec this nested build:
#projects_controller.rb
def new
#account.projects.build
end
So far I have something like this:
#projects_controller_spec.rb
describe ProectssController do
describe "GET new" do
let(:account) { mock_model(Account) }
let(:project) { mock_model(Project).as_null_object }
before do
Account.stub(:find_by_subdomain!).and_return(account)
#Project.should_receive(:build).with(:account_id => account.id).and_return(project)
end
it "assigns #project" do
get :new
assigns[:project].should eq(project)
end
end
end
Not sure how I should be specing this...
The assignment is missing in ProjectsController#new. Should be:
def new
# ...
#project = #account.projects.build
# ...
end
Then you can stub returning a double as you intended:
it "assigns #project" do
account = mock_model(Account)
Account.stub(:find_by_subdomain!).and_return(account)
project = account.stub_chain(:projects,:build) { mock_model(Project) }
get :new
assigns(:project).should == project
end
In general, I recommend stubbing and mocking as little as possible. I recommend using something like Factory Girl to create real database objects for the tests to interact with. That means that Account and Project would be real ActiveRecord classes, and then #account would be a real AR object with a projects association that works just like it does in production. This is important, since otherwise you're just testing to the implementation you've written, and haven't actually tested that your code functions when it's actually using ActiveRecord.
Once you can do that, I would recommend simply checking things you care about for the Project model, e.g.:
assigns[:project].should be_instance_of(Project)
assigns[:project].should be_new_record
assigns[:project].account.should == logged_in_user
Hope this helps!

Resources