Rspec does not seem to be targeting the correct controller - ruby

Getting really bizarre rspec behavior in one of my controller specs.
It's best to illustrate. In rubymine, when I set a breakpoint, this happens:
#rspec test
describe Api::V1::UsersController do
let(:user) { FactoryGirl.create(:user) }
describe "#show" do
it "responds successfully" do
get 'show', id: user.id
response.should be_success
end
end
#controller
class Api::V1::UsersController < AuthenticatedController
def show # !!! RubyMine breakpoint will stop execution here !!!
user = User.find(params[:id])
user_hash = User.information(user, current_user)
respond_to do |format|
format.json { render json: user_hash.to_json }
end
end
So the above works as expected.
But, now this test fails.
#rspec test
describe UsersController do
let(:user) { FactoryGirl.create(:user, is_admin: false) }
describe "#show" do
it "redirects non-admin" do
get 'index'
response.should redirect_to user_path(user)
end
end
#controller
class UsersController < AuthenticatedController
def index # !!! Breakpoint is never hit !!!
#users = User.all
respond_to do |format|
if current_user.is_admin
format.html
format.json { render json: #users }
else
redirect_to user_path(current_user) and return
end
end
end
By the way, this is the result:
Expected response to be a redirect to <http://test.host/users/625> but was a redirect to <https://test.host/users>
None of my breakpoints in controller methods in UsersController are hit. BUT all controller methods are hit if I set breakpoints in API::V1::UsersController.
Any guidance is greatly appreciated. I'm really at a loss of how to debug this.

Sorry, this question was more out of frustation than anything. But I finally figured out what was going on. Hint: tailing the test.log is a good idea.
I was forcing ssl on the controller. The request rspec sent is http. ActionController::ForceSSL redirects the request to https and to the same controller#action. However, at this point, the rspec test was finished and failed the test because it only sees the redirection back to the same controller#action.
So in a before(:each) or something similar, use this: request.env['HTTPS'] = 'on'. All tests work as expected now.

I think maybe you're stepping outside of the domain of rspec here with regards to redirect testing. May I suggest using capybara and rspec?
My sources:
Rspec - Rails - How to follow a redirect
http://robots.thoughtbot.com/post/33771089985/rspec-integration-tests-with-capybara

Related

Testing Create method from controller

I am looking to test my create method in my controller, checking that my validations and flash messages set correctly. What I have so far is
it 'is invalid with invalid formatted email' do
#message = Message.new(FactoryGirl.attributes_for(:message_invalid_email))
#message.valid?
expect(#message.errors[:email].first).to eq("Don't forget to add your email address")
end
But I have seen others setup their test like
#message = post :create, message: { params1: value1 }
What is the difference here and how should I be testing this?
And also when I try and test that a flash success has been set (using shoulda gem)
it 'is valid with correct parameters' do
#message = Message.new(FactoryGirl.attributes_for(:valid_message))
expect(#message).to be_valid
expect(controller).to set_flash[:success]
end
I get this error
Expected the flash[:success] to be set, but no flash was set
This is my controller
def create
#message = Message.new(message_params)
respond_to do |format|
if #message.valid?
format.js { flash.now[:success] = "Thanks for your message, I'll be in touch soon" }
else
format.js { flash.now[:alert] }
end
end
end
The difference between your testing method and the other is that, in the first case, you are testing the model, and, in the second case, you are testing the controller. Also, validations should be tested on the model; flash messages and actions behaviour, within the controller.
I think that you are not calling your create action with expect(controller).to set_flash[:success]. Perhaps you should try something like the following block:
it 'is valid with correct parameters' do
post :create, message: { params1: value1 } # Whatever POST action
expect(flash.now[:success].now).to be_present
end
Is your controller working as expected? format.js is returning a string (flash.now[:success] or flash.now[:alert] instead of javascript code. Perhaps you should use format.text, or return nothing with render status: :created, nothing: true.

testing private before filter that relies on request body in rspec

I have the following controller:
class ApiController < ApplicationController
before_filter :authenticate_user_from_token!
private
def authenticate_user_from_token!
#json = JSON.parse(request.body.read)
--auth logic goes here extracting user credentials goes here--
request.body.rewind
if auth_valid
authenticate
else
render nothing: true, status: :unauthorized
end
end
end
Testing this method has proven to be surprisingly difficult. I have tried the following approaches:
1) Sending the private method directly:
#controller.send(authenticate_user_from_token!)
The flaw here, is that I am unsure how to mock out request.body to contain valid/invalid credentials. I have tried the following:
before do
class Hash
def body
self['body']
end
end
#request = {}
#request['body'] =StringIO.new({auth_goes_here}.to_json)
end
However, in the method, request still gets overriden with a brand new ActionController::TestRequest.
2) Posting directly:
before do
post :authenticate_user_from_token!, my_preconstructed_credentials, format: :json
end
which results in:
*** AbstractController::ActionNotFound Exception: The action 'authenticate_user_from_token!' could not be found for ApiController
3) Defining an exposed method at runtime:
before do
#controller.class_eval <<-RUBY_EVAL
public
def update
end
RUBY_EVAL
end
followed by post :update, which still results in *** NoMethodError Exception: undefined methodfoobar' for #`
My question is: how can I test a private before filter on a controller that depends on request.body? I realize I could mock out request and body in my third approach, but I would still require it to respond to read/rewind. How is this kind of method usually tested?
In the end, the following worked:
before do
class ::TestApiController < ApiController
def hello
render nothing: true
end
end
Rails.application.routes.draw do
match 'hello', to: 'test_api#hello'
end
#controller = TestApiController.new
#request.env['RAW_POST_DATA'] = my_awesome_json
post :hello
end

Rescuing an ActiveRecord::RecordNotUnique in Rails 3.2

I am new to ruby and I want to ensure that the combination of three columns in a model will be unique. I tried using validates_uniqueness_of with scopes but it didn't work as intended. So i added a unique index to my db which raises an ActiveRecord::RecordNotUnique exception every time an already existing combination of values is saved. The problem is that i am not sure on how to handle the exception. In my controller i altered the update action to:
def update
#patient_history = PatientHistory.find(params[:id])
#patient_history.save!
respond_to do |format|
format.html { redirect_to #patient_history, notice: 'Patient history was successfully updated.' }
format.json { head :no_content }
end
rescue ActiveRecord::RecordNotUnique
respond_to do |format|
format.html { render action: "edit" }
format.json { render json: #patient_history.errors, status: :unprocessable_entity }
end
end
end
This code does not saves the changes and redirects to the show page of my model (:patient_history) with the notice "Patient history was successfully updated". What i am trying to do is for the controller to redirect to the edit page and flash a message at the top of the page about the error (much like validates_uniqueness of would do).
Thanks in advance
How did you try using validates_uniqueness_of? You should use this to make sure that this combination is unique, like this:
validates_uniqueness_of :column1, :scope => [:column2, :column3]

Why does respond_with JSON not work?

I'm having a problem when trying to use the return to in a rails controller. This is not working:
class UsersController < ApplicationController
respond_to :json
def create
#user = User.create params[:user_info]
respond_with #user
end
end
This works:
class UsersController < ApplicationController
respond_to :json
def create
#user = User.create params[:user_info]
respond_with #user do |format|
format.json { render json: #user.to_json }
end
end
end
Why? This is the error I have in the server's log when using the one that doesn't work:
NoMethodError (undefined method `user_url' for #<UsersController:0x007fd44d83ea90>):
app/controllers/users_controller.rb:7:in `create'
My route is:
resources :users, :only => [:create]
responds_with tries to redirect to user_url, so it looks for a show method in your user controller, which you don't have, since your route is limited to the create method only. Since the create method redirects to the show method by default this doesn't work. But in your second version you are actually rendering something, so no redirection happens.
You can give a :location option to respond_with if that's what you want, like so:
respond_with(#user, :location => home_url)
or use the render version as you do in your second version.

Couldn't find <object> without an ID rails 3.0.1

Unfortunately, this is my second post in as many days. So the application worked fine with mysql and rails 3.0.3 but I found out that I needed to use MSSQL so I had to downgrade rails to 3.0.1.
In a nutshell, I copied the show.html.erb as show2.html.erb and created a new method which is a copy of the show method. Then I created a route match.
my controller
class fathersController < ApplicationController
def show
#father= Father.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #father}
end
end
def show2
#father= Father.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #father}
end
end
end
routes.rb
resources :fathers do
match '/show2' => 'fathers#show2'
resources :kids
end
when I call
http://127.0.0.1:3000/father/1
I get the show view but when I call
http://127.0.0.1:3000/father/1/show2
I get the following error
Couldn't find father without an ID
The request Parameters come back as
{"father_id"=>"1"}
so I know that the problem is that the app is passing the id as father_id but how do I fix it? Any help would be appreciated.
There are two problems.
You're trying to use a non-resourceful route on a route that actually should be resourceful.
It looks like you're trying to send /show2 to a controller named hospitals, when your action is actually specified on the fathers controller.
This should do the trick:
resources :fathers do
get :show2, :on => :member
resources :kids
end
You can also write the above as:
resources :fathers do
member do
get :show2
end
resources :kids
end

Resources