rspec rails mocking session hash - ruby-on-rails

I am trying to mock out the session hash for a controller like so:
it "finds using the session[:company_id]" do
session.should_receive(:[]).with(:company_id).and_return 100
Company.should_receive(:find).with(100)
get 'show'
end
When I call get 'show' it states:
received :[] with unexpected arguments
expected: (:company_id)
got: ("flash")
The controller code looks like:
def show
company_id = session[:company_id]
#company = Company.find params[company_id]
end
I have also simply tried setting
it "finds using the session[:company_id]" do
session[:company_id]= 100
Company.should_receive(:find).with(100)
get 'show'
end
but then get an issue about:
expected: (100)
got: (nil)
Anyone have ideas why?

I just ran into this. I couldn't manage to get should_receive to not interfere with the flash stuff.
But this let me test the behavior I was looking for:
it "should redirect to intended_url if set" do
request.env['warden'] = double(:authenticate! => true)
session.stub(:[]).with("flash").and_return double(:sweep => true, :update => true, :[]= => [])
session.stub(:[]).with(:intended_url).and_return("/users")
post 'create'
response.should redirect_to("/users")
end
Hope that helps...

I could not figure out how to mock the session container itself, however in most cases simply passing session data with request should be enough. So the test would split into two cases:
it "returns 404 if company_id is not in session" do
get :show, {}, {}
response.status.should == 404 # or assert_raises depending on how you handle 404s
end
it "finds using the session[:company_id]" do
Company.should_receive(:find).with(100)
get :show, {}, {:company_id => 100}
end
PS: forgot to mention I'm using some customized helpers from this snippet.

try this:
session.expects(:[]).with(has_entries('company_id' => 100))

It's because you fetch flash session from your controller. So define it. Flash is save in session.
it "finds using the session[:company_id]" do
session.stub!(:[]).with(:flash)
session.should_receive(:[]).with(:company_id).and_return 100
Company.should_receive(:find).with(100)
get 'show'
end

Related

how to make a custom json rails routes and make the tests pass

Edit 2: OMG I AM SO STUPID. In my spec I have a let(:response) {MyModel.create()} so thats why its failing. Going to delete post
(edited for clarity)
In my routes file
root "search_email#index"
get "search_email/retrieve_last_user_survey" => "search_email#retrieve_last_user_survey"
Controller
class SearchEmailController < ApplicationController
def retrieve_last_user_survey
render :json => "")
end
end
Spec file
require "rails_helper"
RSpec.describe SearchEmailController, type: :controller do
describe 'GET #retrieve_last_user_survey' do
before do
get :retrieve_last_user_survey, :params => { :email => 'abc#abc.com'}
end
it "returns http success" do
expect(response).to have_http_status(:success)
end
end
end
When try to run my test, i get this
Failure/Error: expect(response).to have_http_status(:success)
expected a response object, but an instance of Relational::Response (custom model name) was received
I have no idea why I am not getting a response object, I know I am hitting the controller method cause I inserted puts and I can see it.
Also on a semi related note. If i create a button that hits this route. why does it redirect me to a show route. I thought it would just return some http request that i can see in the dev console. I know cause said I dont have a show route or a show template.
It's not meant to be facetious, but to get the test to pass, replace the render line in the controller with:
head :ok
Does the test pass? Probably. So now add some expectation on the content header, and then finally the content itself.
If you break it down into small pieces, you should find the problem. It's not obvious from what you've shared, we can't see into the controller method.

In Rails Controller testing, is there a way to pass query (non-routing) parameters?

I'm writing controller tests in Rails and RSpec, and it seems from reading the source code of ActionController::TestCase that it's not possible to pass arbitrary query parameters to the controller -- only routing parameters.
To work around this limitation, I am currently using with_routing:
with_routing do |routes|
# this nonsense is necessary because
# Rails controller testing does not
# pass on query params, only routing params
routes.draw do
get '/users/confirmation/:confirmation_token' => 'user_confirmations#show'
root :to => 'root#index'
end
get :show, 'confirmation_token' => CONFIRMATION_TOKEN
end
As you may be able to guess, I am testing a custom Confirmations controller for Devise. This means I am jacking into an existing API and do not have the option to change how the real mapping in config/routes.rb is done.
Is there a neater way to do this? A supported way for get to pass query parameters?
EDIT: There is something else going on. I created a minimal example in https://github.com/clacke/so_13866283 :
spec/controllers/receive_query_param_controller_spec.rb
describe ReceiveQueryParamController do
describe '#please' do
it 'receives query param, sets #my_param' do
get :please, :my_param => 'test_value'
assigns(:my_param).should eq 'test_value'
end
end
end
app/controllers/receive_query_param_controller.rb
class ReceiveQueryParamController < ApplicationController
def please
#my_param = params[:my_param]
end
end
config/routes.rb
So13866283::Application.routes.draw do
get '/receive_query_param/please' => 'receive_query_param#please'
end
This test passes, so I suppose it is Devise that does something funky with the routing.
EDIT:
Pinned down where in Devise routes are defined, and updated my example app to match it.
So13866283::Application.routes.draw do
resource :receive_query_param, :only => [:show],
:controller => "receive_query_param"
end
... and spec and controller updated accordingly to use #show. The test still passes, i.e. params[:my_param] is populated by get :show, :my_param => 'blah'. So, still a mystery why this does not happen in my real app.
Controller tests don't route. You are unit-testing the controller--routing is outside its scope.
A typical controller spec example tests an action:
describe MyController do
it "is successful" do
get :index
response.status.should == 200
end
end
You set up the test context by passing parameters to get, e.g.:
get :show, :id => 1
You can pass query parameters in that hash.
If you do want to test routing, you can write routing specs, or request (integration) specs.
Are you sure there isn't something else going on? I have a Rails 3.0.x project and am passing parameters.. well.. this is a post.. maybe it's different for get, but that seems odd..
before { post :contact_us, :contact_us => {:email => 'joe#example.com',
:category => 'Category', :subject => 'Subject', :message => 'Message'} }
The above is definitely being used in my controller in the params object.
I am doing this now:
#request.env['QUERY_STRING'] = "confirmation_token=" # otherwise it's ignored
get :show, :confirmation_token => CONFIRMATION_TOKEN
... but it looks hacky.
If someone could show me a neat and official way to do this, I would be delighted. Judging from what I've seen in the source code of #get and everything it calls, there doesn't seem to be any other way, but I'm hoping I overlooked something.

Understanding Rails 3's respond_with

Utilizing ActionController's new respond_with method...how does it determine what to render when action (save) is successful and when it's not?
I ask because I'm trying to get a scaffold generated spec (included below) to pass, if only so that I can understand it. The app is working fine but, oddly, it appears to be rendering /carriers (at least that's what the browser's URL says) when a validation fails. Yet, the spec is expecting "new" (and so am I, for that matter) but instead is receiving <"">. If I change the spec to expect "" it still fails.
When it renders /carriers that page shows the error_messages next to the fields that failed validation as one would expect.
Can anyone familiar with respond_with see what's happening here?
#carrier.rb
validates :name, :presence => true
#carriers_controller.rb
class CarriersController < ApplicationController
respond_to :html, :json
...
def new
respond_with(#carrier = Carrier.new)
end
def create
#carrier = Carrier.new(params[:carrier])
flash[:success] = 'Carrier was successfully created.' if #carrier.save
respond_with(#carrier)
end
Spec that's failing:
#carriers_controller_spec.rb
require 'spec_helper'
describe CarriersController do
def mock_carrier(stubs={})
(#mock_carrier ||= mock_model(Carrier).as_null_object).tap do |carrier|
carrier.stub(stubs) unless stubs.empty?
end
end
describe "POST create" do
describe "with invalid params" do
it "re-renders the 'new' template" do
Carrier.stub(:new) { mock_carrier(:save => false) }
post :create, :carrier => {}
response.should render_template("new")
end
end
end
end
with this error:
1) CarriersController POST create with invalid params re-renders the 'new' template
Failure/Error: response.should render_template("new")
expecting <"new"> but rendering with <"">.
Expected block to return true value.
# (eval):2:in `assert_block'
# ./spec/controllers/carriers_controller_spec.rb:81:in `block (4 levels) in <top (required)>'
tl:dr
Add an error hash to the mock:
Carrier.stub(:new) { mock_carrier(:save => false,
:errors => { :anything => "any value (even nil)" })}
This will trigger the desired behavior in respond_with.
What is going on here
Add this after the post :create
response.code.should == "200"
It fails with expected: "200", got: "302". So it is redirecting instead of rendering the new template when it shouldn't. Where is it going? Give it a path we know will fail:
response.should redirect_to("/")
Now it fails with Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/carriers/1001>
The spec is supposed to pass by rendering the new template, which is the normal course of events after the save on the mock Carrier object returns false. Instead respond_with ends up redirecting to show_carrier_path. Which is just plain wrong. But why?
After some digging in the source code, it seems that the controller tries to render 'carriers/create'. There is no such template, so an exception is raised. The rescue block determines the request is a POST and there is nothing in the error hash, upon which the controller redirects to the default resource, which is the mock Carrier.
That is puzzling, since the controller should not assume there is a valid model instance. This is a create after all. At this point I can only surmise that the test environment is somehow taking shortcuts.
So the workaround is to provide a fake error hash. Normally something would be in the hash after save fails, so that kinda makes sense.

Rails inherited resources usage

I'm using Inherited Resources for my Rails 2.3 web service app.
It's a great library which is part of Rails 3.
I'm trying to figure out the best practice for outputting the result.
class Api::ItemsController < InheritedResources::Base
respond_to :xml, :json
def create
#error = nil
#error = not_authorized if !#user
#error = not_enough_data("item") if params[:item].nil?
#item = Item.new(params[:item])
#item.user_id = #user.id
if !#item.save
#error = validation_error(#item.errors)
end
if !#error.nil?
respond_with(#error)
else
respond_with(#swarm)
end
end
end
It works well when the request is successful. However, when there's any error, I get a "Template is missing" error. #error is basically a hash of message and status, e.g. {:message => "Not authorized", :status => 401}. It seems respond_with only calls to_xml or to_json with the particular model the controller is associated with.
What is an elegant way to handle this?
I want to avoid creating a template file for each action and each format (create.xml.erb and create.json.erb in this case)
Basically I want:
/create.json [POST] => {"name": "my name", "id":1} # when successful
/create.json [POST] => {"message" => "Not authorized", "status" => 401} # when not authorized
Thanks in advance.
Few things before we start:
First off. This is Ruby. You know there's an unless command. You can stop doing if !
Also, you don't have to do the double negative of if !*.nil? – Do if *.present?
You do not need to initiate a variable by making it nil. Unless you are setting it in a before_chain, which you would just be overwriting it in future calls anyway.
What you will want to do is use the render :json method. Check the API but it looks something like this:
render :json => { :success => true, :user => #user.to_json(:only => [:name]) }
authorization should be implemented as callback (before_filter), and rest of code should be removed and used as inherited. Only output should be parametrized.Too many custom code here...

Why is this controller test on a create action failing?

I'm getting a failing test here that I'm having trouble understanding. I'm using Test::Unit with Shoulda enhancement. Action in users_controller.rb I'm trying to test...
def create
unless params[:user][:email] =~ / specific regex needed for this app /i
# ...
render :template => 'sessions/new'
end
end
Test...
context 'on CREATE to :user' do
context 'with invalid email' do
setup { post :create, { 'user[email]' => 'abc#abcd' } }
should_respond_with :success
end
# ...
end
Fails because "response to be a <:success>, but was <302>". How is it 302?
Change action to...
def create
render :template => 'sessions/new'
end
Test still fails.
#Ola: You're wrong: POST is connected to create. PUT is normally connected to update.
A :forbidden is quiet odd though. Here are some suggestions to find the problem (I've never used Shoulda, but I don't think it is a problem with Shoulda.
Make sure the route is defined in config/routes.rb and check with rake routes
Do you have any before_filters that could be responsible for that behaviour (login filter, acts_as_authenticated etc..)? Checkout log/test.log. A halt in the filter chain shows up there.
Print out the response body puts response.body to see what you get returned.
Hope this helps.
If you're using default REST-ful URLs, you probably should use PUT, not POST... Since PUT is connected to create, POST to that URL will give you an unauthorized and redirect.

Resources