I have written the following test which tries to update an already published post to saved. Meaning the user has published a post and now wants to mark it as saved. Which I do not allow.
it "should not update a post from published to saved" do
#post = FactoryGirl.create(:post, blog_id: #blog.id, saved: false, published: true)
put :update, :id => #post.id, post: {title: 'Sample Title', content: #post.content, saved: true}
expect(response.status).to eql 422
end
This particular test states:
AbstractController::DoubleRenderError:
Render and/or redirect were called multiple times in this action.
Which should not be the case because if we look at the method thats throwing it:
def post_controller_save(post, params)
do_not_save_published_post(post, params)
binding.pry
publish_post(post, params)
if post.save
set_tags(post, params)
set_categories(post, params)
render json: post, status: 200
else
render json: {:errors => post.errors}, status: 422
end
end
The first check we do is do_not_save_published_posts(post, params) (notice the binding.pry after it).
So if we follow this, we see the method in question (which is a private method):
def do_not_save_published_post(post, params)
if (post.published && params[:post][:saved])
binding.pry
render json: {:errors => 'Cannot save a published post.'}, status: 422
return
end
end
This method checks if the post in question is published. and that the params for the post object contains a saved parameter. If both of these are true, we will render an error and return. The key here is to notice this binding.pry.
In the test, it reaches this break point and then typing exit causes the return to be skipped and the binding.pry out side this method to be caught.
This SHOULD NOT HAPPEN. It should render me json with a status of 422. Is the fact that this, do_not_save_published_post is a private method a contributing factor?
Your call to do_not_save_published_post is just an ordinary method call, and returning from this method will behave like normal Ruby in that it will continue executing code after the method call.
You probably want to change your do_not_save_published_post to published_post?, move it to your model, have it return a boolean, and do your render logic in the controller.
Related
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.
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
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
I'd like a controller method to respond by disabling/denying layout if the request happens to be an ajax request (i.e., request.xhr? == true).
The following doesn't seem to work -- it returns the layout nonetheless
class FooController < Ramaze::Controller
layout :default
def bar
if request.xhr?
layout nil
"return something here" #just return this string without the layout
else
... #return with full layout
end
end
You can bypass view and layout rendering by calling respond! like this :
respond!(body, status, 'Content-Type' => 'whatever/foo')
Where body is your (json ?) body, status the HTTP response code.
You can then provide a hash with returned headers.
However, you might be interested in provides that can handle requests differently if they end, for instance, with '.json'. See http://ramaze.net/documentation/file.views.html#View_Mapping for more info on this.
Hey there, im a little bit confused about handling invalid user authentication request, at login controller. So, i already have modified login view, but cant figure out where to put the exception handling block. It should work like this: you login - if its incorrect you will see warning message at /login .
Any ideas ?
What strategy have you chosen ? In my custom Strategy, I call the class method 'authenticate' on my User class:
class User
def self.authenticate(login, password)
u = User.first(:conditions => ['email = ?', login]) # find a user with this login
if u && u.authenticated?
return u
else
nil
end
end
end
Also, you might want to look at the source code of merb-auth-more/mixins/salted_user which is a module that is automatically mixed into your User class.
you would put your exception handling action in the exceptions controller
# handle NotAuthorized exceptions (403)
def not_authorized
render :format => :html
end
and to customise the view you would create a template in app/views/exceptions/not_authorized.html.haml