How can I stub a controller instance variable using RSpec? - ruby-on-rails-3

new_controller_spec.rb
require 'spec_helper'
describe NewController do
describe 'actions' do
describe 'POST /franchise_opportunities' do
it 'renders v3/franchise_opportunities when domain id 5' do
domain = double(Domain, mrdeliverycom?: true)
controller.instance_variable_set(:#domain, domain)
post :franchise_opportunities, name: 'v'
response.should render_template('v3/franchise_opportunities')
end
end
end
end
new_controller.rb
# -*- encoding : utf-8 -*-
class NewController < ApplicationController
before_filter { #domain = ... }
def franchise_opportunities
t = Logger.new(STDOUT)
t.debug '=============================================='
t.debug #domain
t.debug #domain.mrdeliverycom?
t.debug '=============================================='
render :layout=>'v3', :template=>'v3/franchise_opportunities' and return if #domain && #domain.mrdeliverycom?
#errors={}
#post = params.clone
...
end
end
Log
==============================================
#<Domain id: 1, name: "hettingersawayn.org", email: "alfonso.carroll#eichmanngrady.name", created_at: "2015-06-06 13:11:49", updated_at: "2015-06-06 13:11:49", domain_name: "ziemann.info8732", logo: nil, favicon: nil, slogan: nil, meta_title: nil, meta_keywords: nil, meta_description: nil, about_us: nil, smtp_server: "smtp.gmail.com", smtp_port: "587", smtp_username: nil, smtp_password: nil, smtp_authentication: "plain", smtp_tls: true, background: nil, source: "web", mobile_logo: nil, show_chef: false, ios_icon: nil, intro_text: nil>
nil
==============================================
Seems #domain variable was set in spec method not overridden #domain variable was set in before_filter of the controller.

In fact the instance variable is set to the value that you provide in the example, but that happens before the before_filter executes, so it ends up being set again.
You could move the initialization from the before_filter into a method in the controller and stub that instead:
before_filter { #domain = get_domain }
...
protected
def get_domain
...
end
...
expect(controller).to receive(:get_domain).and_return("stubbed value")

Related

While testing Devise with rspec, it acts like I am not logged in

I'm using Rails 3.2.13, devise 2.2.4, and rspec 2.13.2. I have a multi-tenant app in which the default scope only shows current_user's items. Everything seems to work in development, but my tests are having issues.
Problem: When I create a meal in my test, it shows user_id => nil. In my development environment everything works, though. Also, when I go into debug mode, when I check the #controller.current_user, it shows user_id 1 is logged in. Since in my development environment the user_id is being assigned automatically when I create an object, I can't figure out why it's not doing it during tests. Any ideas?
Here's my abbreviated meals_controller_spec file:
describe MealsController do
include Devise::TestHelpers
describe "When logged in as user," do
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#email = "someone#test.com"
#password = "password"
#user = User.create(email: #email, password: #password)
sign_in #user
end
describe "DELETE destroy" do
it "destroys the requested meal" do
meal = Meal.create!("name" => "", "servings" => "2", "meal_type" => "dinner")
expect { delete 'destroy', {:id => meal.id} }.to change(Meal, :count).by(-1)
end
end
end
end
Here's part of my model:
class Meal < ActiveRecord::Base
belongs_to :user
default_scope { where(user_id: User.current_id) }
Here's part of my controller:
class MealsController < ApplicationController
before_filter :authenticate_user!
def destroy
#meal = Meal.find(params[:id])
#meal.destroy
redirect_to meals_url, :notice => "Successfully deleted #{#meal.name}."
end
end
And I think my application controller could be relevant:
class ApplicationController < ActionController::Base
protect_from_forgery
around_filter :scope_current_tenant
private
def scope_current_tenant
User.current_id = current_user.id
yield
rescue Exception
yield
ensure
User.current_id = nil
end
end
Here's some output when I debug in the middle of that test:
(rdb:1) #controller.current_user.id
1
(rdb:1) Meal.create!(name: "meal", meal_type: "dinner", servings: 2)
#<Meal id: 2, name: "meal", meal_type: "dinner", servings: 2, user_id: nil, created_at: "2013-06-17 06:07:34", updated_at: "2013-06-17 06:07:34">
(rdb:1) Meal.create!(name: "meal", meal_type: "dinner", servings: 2, user_id: 1)
#<Meal id: 3, name: "meal", meal_type: "dinner", servings: 2, user_id: 1, created_at: "2013-06-17 06:12:18", updated_at: "2013-06-17 06:12:18">
(rdb:1) Meal.find(3).destroy
*** NoMethodError Exception: undefined method `destroy' for nil:NilClass
So I guess I'm wondering why the user in my test appears to be nil, even though I am using the Devise test helpers and signing in, and in my dev environment everything works.
Edit:
Here are some more debugger results:
(rdb:1) Meal.find(4)
*** ActiveRecord::RecordNotFound Exception: Couldn't find Meal with id=4 [WHERE "meals"."user_id" IS NULL]
(rdb:1) Meal.find(4).destroy
*** ActiveRecord::RecordNotFound Exception: Couldn't find Meal with id=4 [WHERE "meals"."user_id" IS NULL]
(rdb:1) Meal.where(:user_id => 1).find(3)
#<Meal id: 3, name: "meal", meal_type: "dinner", servings: 2, user_id: 1, created_at: "2013-06-17 06:12:18", updated_at: "2013-06-17 06:12:18">
It appears that the "current user" is in fact user_id => nil, per the sql snippets. So why would that be if I used the Devise test helper sign_in function?

Issue using RSpec to pass parameters to mocked model method in Rails controller action

I'm trying to mock a user models save method to return false, and simulate what the controller would do in case of validation failure.
My spec looks like:
require 'spec_helper'
describe UsersController do
describe 'POST create_email_user' do
it 'responds with errors json if saving the model fails' do
#params = {
:last_name => 'jones',
:email => 'asdfasdfasfd#'
}
User.stub(:new_email_user) .with(#params) .and_return(#mock_user)
#mock_user.stub(:save) .and_return(false)
post :create_email_user, :user => #params, :format => :json
response.body.should == #mock_user.errors.as_json
response.status.should == :unprocessable_entity
end
end
end
Controller action looks like:
class UsersController < ApplicationController
def create_email_user
#user = User.new_email_user(params[:user])
if #user.save
# code left out for demo purposes
else
render json: #user.errors, status: :unprocessable_entity
end
end
end
When running this spec, I get the error that basically says the expected call to new_email_user got the wrong arguments:
1) UsersController POST create_email_user responds with errors json if saving the model fails
Failure/Error: post :create_email_user, :user => #params, :format => :json
<User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: string, last_sign_in_ip: string, created_at: datetime, updated_at: datetime, uid: string, provider: string, first_name: string, last_name: string, registration_id: integer, gender: string, authentication_token: string) (class)> received :new_email_user with unexpected arguments
expected: ({:last_name=>"jones", :email=>"asdfasdfasfd#"})
got: (["last_name", "jones"], ["email", "asdfasdfasfd#"])
Please stub a default value first if message might be received with other args as well.
It seems like rails converts the has to an array of key value pairs, while my code is just using the straight hash in the setup.
How can I simulate this converting to an array, or am I doing else wrong?
Drop the .with stub on user, since it isn't really relevant to the test and it is causing problems.
Without the .with the stub will return that value for any arguments.
So:
User.stub(:new_email_user).and_return(#mock_user)
I would then have a separate spec which ensures that:
User.should_receive(:new_email_user).with(#params)
And diagnose that problem in its own, narrow spec.

Testing rails controller with rspec: value is same, but object id is different

I try to test a rails controller with rspec like this:
require 'spec_helper'
describe NewsController do
describe "GET 'edit'" do
before(:each) do
#news_1 = FactoryGirl.create(:news_1)
get :edit, { :id => #news_1.id }
end
it { response.should be_success }
it { assigns(:news).should eq(#news_1) }
it { response.should render_template(:edit) }
end
end
But, I got this error.
Failures:
1) NewsController GET 'edit'
Failure/Error: it { assigns(:news).should eq(#news_1) }
expected: #<News id: 1, title: "news_title_1", contents: "news_contents_1", display: nil, created_at: "2012-11-29 07:24:49", updated_at: "2012-11-29 07:24:49", news_date: nil, orion: false, pw: false, op: false, pickup: false, info_1: nil, info_2: nil, info_3: nil, info_4: nil, info_5: nil, del: false, place: nil, contact: false>
got: #<News id: 1, title: "news_title_1", contents: "news_contents_1", display: nil, created_at: "2012-11-29 07:24:49", updated_at: "2012-11-29 07:24:49", news_date: nil, orion: false, pw: false, op: false, pickup: false, info_1: nil, info_2: nil, info_3: nil, info_4: nil, info_5: nil, del: false, place: nil, contact: false>
(compared using ==)
Diff:#<News:0x000001030b55c8>.==(#<News:0x000001033126b8>) returned false even though the diff between #<News:0x000001030b55c8> and #<News:0x000001033126b8> is empty. Check the implementation of #<News:0x000001030b55c8>.==.
# ./spec/controllers/news_controller_spec.rb:73:in `block (3 levels) in <top (required)>'
I think these values are same, but object id is different.
So this test fails...
How do I solve this error?
Had this problem myself. My workaround was comparing the attributes, i.e.
it { assigns(:news).attributes.should eq(News.last.attributes) }
I think the problem here is how the 'eq' does the comparison between two objects. you might want to use == in this scenario. You can see a detailed explanation in this question here
you should do as follow
require 'spec_helper'
describe NewsController do
describe "GET 'edit'" do
before(:each) do
#news_1 = FactoryGirl.create(:news_1)
get :edit, { :id => #news_1.id }
end
it { response.should be_success }
it { assigns(:news).should eq(News.last) }
it { response.should render_template(:edit) }
end
end
== and eq are different. To test your method when it retrieves the object, then use should == or its equivalent a.should eql
I had this problem comparing an array and I solved it with:
expect(assigns(:receipts)).to match_array(receipts)

Rspec and Capybara object matching

Using Rails 3.0.5, RSpec 2 and Capybara 0.4.1.2 and I'am trying to write a controller spec for my SessionsController#new action.
it "assigns the empty session to a variable" do
get :new
assigns(:session).should == ActiveRecord::Base::Session.new
end
I'm using the ActiveRecord::Base namespace as it seems to clash with the Capybara Session class when I don't.
Here is the SessionsController:
class SessionsController < ApplicationController
def new
#session = Session.new
end
end
RSpec doesn't seem to understand these are the same objects. Here is what my test returns:
Failure/Error: assigns(:session).should == ActiveRecord::Base::Session.new
expected: #<Session id: nil, session_id: nil, data: nil, created_at: nil, updated_at: nil>
got: #<Session id: nil, session_id: nil, data: nil, created_at: nil, updated_at: nil> (using ==)
Diff:
# ./spec/controllers/sessions_controller_spec.rb:17:in `block (3 levels) in <top (required)>'
Any hints?
The problem is that if you do
ActiveRecord::Base::Session.new == ActiveRecord::Base::Session.new
You would get false, as both these objects have a separate object_id.
Try this:
assigns(:session).should be_an(ActiveRecord::Base::Session)

How to create a nested form in Rails 3 that works? ( I'm getting a routing error. )

I have a Client and ProposalRequest model that look like this:
class Client < ActiveRecord::Base
has_many :proposal_requests
accepts_nested_attributes_for :proposal_requests, :allow_destroy => true
end
class ProposalRequest < ActiveRecord::Base
belongs_to :client
end
In my my routes file, I included the nested routes, as usual.
resources :clients do
resources :proposal_requests
end
And this is my form so far:
-semantic_form_for [Client.new, ProposalRequest.new] do |f|
=f.inputs
=f.buttons
But after this, I'm stuck because of this error.
No route matches {:controller=>"proposal_requests", :client_id=>#<Client id: nil, name: nil, title: nil, organization: nil, street_address: nil, city: nil, state: nil, zip: nil, phone: nil, email: nil, status: "interested", how_you_heard: nil, created_at: nil, updated_at: nil>}
Can anyone help me puzzle out this error?
The problem is that your nested route is meant to add a new ProposalRequest to an existing Client. If you want to create a Client and a ProposalRequest at the same time, you need to just use new_client_path and semantic_form_for #client do |f|.
I would recommend you do the following in your clients_controller:
def new
#client = Client.find(params[:id])
#client.proposal_requests.build
end
And in your view:
semantic_form_for #client do |f|
= f.inputs # fields for client
= f.inputs :name => 'Proposal Request', :for => :proposal_requests do |pf|
= pf.input :some_proposal_request_attribute
= f.buttons
Hope this helps. Make sure to look at all the examples at https://github.com/justinfrench/formtastic and do some trial and error to get your form how you want it.