Testing Create method from controller - ruby

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.

Related

Why is it stating I am rendering or calling render twice?

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.

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]

Rspec does not seem to be targeting the correct controller

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

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