Ruby-on-rails test raising InvalidCrossOriginRequest when rendering a JS view - ruby-on-rails

I am testing a controller which has actions rendering views in format .js.erb .
The tests on these actions raise the following error :
Minitest::UnexpectedError: ActionController::InvalidCrossOriginRequest: Security warning: an embedded tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.
The forgery protection causes this error, as when I put an exception on these actions its ok, but I dont want to disable it.
Here is my controller :
class TasksController < ApplicationController
def new
# TODO add blah later
#task = Task.new
render 'tasks/show_form.js.erb'
end
def create
# FIXME there is bug here
#task = Task.new(task_params)
#task.user = current_user
authorize! :create, #task
save_task
end
def edit
#task = Task.find(params[:id])
authorize! :edit, #task
render 'tasks/show_form.js.erb'
end
def update
#task = Task.find(params[:id])
#task.assign_attributes(task_params)
authorize! :update, #task
save_task
end
def destroy
#task = Task.find(params[:id])
authorize! :destroy, #task
#task.destroy
#tasks = Task.accessible_by(current_ability)
end
private
def save_task
if #task.save
#tasks = Task.accessible_by(current_ability)
render 'tasks/hide_form.js.erb'
else
render 'tasks/show_form.js.erb'
end
end
def task_params
params.require(:task).permit(:title, :note, :completed)
end
end
Here is my test for this controller :
require 'test_helper'
class TasksControllerTest < ActionDispatch::IntegrationTest
#include Devise::Test::ControllerHelpers
def setup
#task = tasks(:one)
#password = "password"
#confirmed_user = User.create(email: "#{rand(50000)}#example.com",
password: #password,
confirmed_at: "2020-02-01 11:35:56")
#unconfirmed_user = User.create(email: "#{rand(50000)}#example.com",
password: #password,
confirmed_at: "")
sign_in(user: #confirmed_user, password: #password)
end
test "should get new" do
get new_task_url
assert_response :success
end
test "should create task" do
assert_difference('Task.count') do
post tasks_url, params: {task: {title: 'Dont fail test !'}}
end
assert_redirected_to task_url(Task.last)
end
test "should get edit" do
get edit_task_url(#task)
assert_response :success
end
test "should update task" do
patch task_url(#task), params: {task: {title: 'updated'}}
assert_redirected_to task_url(#task)
article.reload
assert_equal "updated", article.title
end
test "should destroy task" do
assert_difference('Task.count', -1) do
delete task_url(#task)
end
assert_redirected_to tasks_url
end
end
Do any of you has an idea on how to correct this error ?
Thank you in advance

You might try changing your get request to use the same mechanism that the browser would for an AJAX request, a XMLHttpRequest. You can do this in rails testing with xhr
In your case:
xhr :get, new_task_url
This means you aren't bypassing rails defaults just to get your test to pass.

The solution was to put at the beginning of my controller
skip_before_action :verify_authenticity_token, :only => [:edit,
:new]
Hope this can help someone

Related

controller test not passing in rails

My controller test isn't passing. I have a before_acton in my ReviewsController that tests for a current user. If there is none, it shouldn't complete the create action and redirect. But it does create it, despite the session[:user_id] being nil. Here is my code:
it "doesnt create review if there is valid input, but not authenticated" do
video = Video.create(title: "new title", description: "new description")
review = Review.create(content: "content1", rating: 1)
expect(Review.count).to eq(0)
end
In my ReviewsController:
def create
#video = Video.find(params[:video_id])
#review = Review.new(parameters)
rating = params[:review][:rating]
content = params[:review][:content]
#review.content = content
#review.rating = rating
#review.user_id = current_user.id
#review.video = #video
#review.save
redirect_to #video
end
i have a before_action in the Reviews controller that test if a user is authenticated. If the session[:user_id] is nil, then it redirects to the sign_in_path. Here are the methods:
def current_user
#current_user = User.find(session[:user_id]) if session[:user_id]
end
def require_user
redirect_to sign_in_path unless current_user
end
in my reviews_controller_spec.rb file, it shouldn't create the review, because it shouldn't get past the before_action :require_user
Why is the review object still being created? Why doesn't the before_action filter stop it? The test fails and just says it expected Review.count to be 0, but got 1.
# config/routes.rb
resources :videos, shallow: true do
resources :reviews
end
There is no need to bind params to attributes 1-1. In fact doing so will make you controllers excessively verbose plus its boring as hell to type it out.
class ReviewsController < ApplicationController
before_filter :authenticate_user!
def create
#video = Video.find(params[:video_id])
#review = current_user.reviews.new(review_params) do |r|
r.video = #video
end
end
private
# this is mass assignment protection
def review_params
params.require(:review).permit(:rating, :content)
end
end
If you decide to roll you own authentication (which is a bad idea) don't repeat it across your controllers and views:
module AuthenticationHelper
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def sign_in(user)
#current_user = user
session[:user_id] = user.id
end
def sign_out
#current_user = nil
reset_session
end
def authenticate_user!
raise User::NotAuthorized unless current_user
end
end
A key point is that a opt out security model is far better since it eliminates the risk of leaving a route open by omission:
class ApplicationController
include AuthenticationHelper
rescue_from User::NotAuthorized, with: :deny_access!
# use skip_before_action :authenticate_user! if
# you don't want a route / controller to require auth.
before_action :authenticate_user!
private
def deny_access!
redirect_to root_path
end
end
So to test ReviewsController we would do:
describe ReviewsController do
let(:user) { User.create(name: 'joe') }
let(:video) { Video.create(name: 'Cats Gone Wild') }
describe "#create" do
let(:valid_attributes) { { video_id: video, title: "new title", description: "new description" } }
context "when unauthorized" do
it "does not create a review" do
expect { post :create, valid_attributes }.to_not change(Review, :count)
end
end
context "when authorized" do
before { subject.stub(:current_user) { user } }
# ...
end
end
end
Key points here:
use let to setup test dependencies
use expect { action }.to change to test if an action alters the DB. Testing for .count == 0 can lead to false positives.

How to run minitest for controller methods?

post_controller file
class PostsController < ActionController::Base
before_action :authenticate_user!
def index
#post = current_user.posts.paginate(page: params[:page], per_page: 5)
respond_to do |format|
format.html
format.json { render json: #post }
end
end
def new
#post = Post.new
end
def create
#post = current_user.posts.build(post_param)
if #post.save
redirect_to action: 'index'
else
render 'new'
end
post_controller_test
require 'test_helper'
class PostsControllerTest < ActionController::TestCase
include Devise::TestHelpers
def setup
#user = users(:Bob)
#post = Post.new
end #passed
test 'logged in should get show' do
sign_in #user
get :index
assert_response :success
end #passed
test 'not authenticated should get redirect' do
get :index
assert_response :redirect
end #passed
test 'should get index' do
get :index
assert_response :success
assert_not_nil assigns(:posts)
end #failing
test "should destroy post" do
assert_difference('Post.count', -1) do
delete :destroy, id: #post
end
assert_redirected_to posts_path
end #failing
...
devise is setup and working fine but why I am getting 302 error in last two cases. Is it because I am not passing #user parameters to it? I did but it was still throwing the same error. I also checked out my routes file which is fine because post_controller is working fine in development mode.
What I am doing wrong here?
Edit-1
I tried to create test cases for create method
def setup
#user = users(:bob)
#p = posts(:one)
#post = Post.new
end
test 'should create post' do
sign_in #user
assert_difference('Post.count') do
post :create, post: { name: #p.name, value: #p.value}
end
end
I am getting ActionController::ParameterMissing: param is missing or the value is empty: post while in my controller class I do have
params.require(:post).permit(:name, :value, :user_id)
I also have all parameters in my .yml file i.e.
one:
name: 2
value: 3
It looks like you need to sign in before trying the index action. You're also testing the wrong instance variable name. You're testing #posts, but you've defined #post in the controller. Try this test instead:
test 'should get index' do
sign_in #user
get :index
assert_response :success
assert_not_nil assigns(:post)
end

expecting <"edit"> but rendering with <[]>

Hi everyone I'm testing my app controllers and I have a problem. I have tests for update action which fails:
describe "PUT #update" do
before :each do
#car_service = create(:car_service)
end
it "locates the requested #message" do
put :update, id: #car_service, car_addition: attributes_for(:car_service)
assigns(:car_addition).should eq(#car_service)
end
context "valid attributes" do
it "changes #car_service's attributes" do
put :update, id: #car_service, car_addition: attributes_for(:car_service, name: "Test")
#car_service.reload
#car_service.name.should eq("Test")
end
it "redirects to the updated message" do
put :update, id: #car_service, car_addition: attributes_for(:car_service)
should redirect_to admin_car_additions_url
end
end
context "invalid attributes" do
it "does not change #car_addition's attributes" do
put :update, id: #car_service, car_addition: attributes_for(:car_service, name: nil)
#car_service.reload
#car_service.name.should_not be_nil
end
it "re-renders the edit method" do
put :update, id: #car_service, car_addition: attributes_for(:car_addition)
should render_template :edit
end
end
end
when i run this tests only one test not pass("re-renders the edit method") and throw out following error:
Failure/Error: should render_template('edit')
expecting <"edit"> but rendering with <[]>
# ./spec/controllers/admin/car_additions_controller_spec.rb:100:in `block (4 levels) in <top (required)>
My controller looks like this:
module Admin
class CarAdditionsController < ApplicationController
include Admin::BaseController
load_and_authorize_resource
add_breadcrumb I18n.t('car_additions.car_addition.home'), :admin_root_path
add_breadcrumb I18n.t('car_additions.car_additions'), :admin_car_additions_path
def index
end
def new
add_breadcrumb t('car_additions.car_addition.new')
end
def edit
add_breadcrumb t('car_additions.car_addition.edit')
end
def create
if #car_addition.save
flash[:notice] = t("car_additions.created")
redirect_to action: :index
else
add_breadcrumb t('car_additions.car_addition.new')
render :new
end
end
def update
if #car_addition.update(car_addition_params)
flash[:notice] = t("car_additions.updated")
redirect_to action: :index
else
render :edit
end
end
def destroy
#car_additon.destroy
flash[:error] = t("car_additions.destroy")
redirect_to action: :index
end
private
def car_addition_params
params.require(:car_addition).permit(:name, :type, :image,
:image_cache, :remove_image)
end
end
end
I'm using devise and CanCan for authorization. Please help.
I'm pass attributes_for(:car_addition) because this is not valid attributes. When I changed this to:
attributes_for(:car_addition, name: nil) it's still not working...
You should use render_views method in order to have your views rendered in specs:
describe "PUT #update" do
render_views
# ...
end

testing "create" method in ruby with rspec

I have written this controller code in Ruby on Rails
class PostsController < ApplicationController
before_filter :authenticate_user!
def index
#posts = Post.all(:order => "created_at DESC")
respond_to do |format|
format.html
end
end
def create
#post = Post.create(:message => params[:message])
respond_to do |format|
if #post.save
format.html { redirect_to posts_path }
format.js
else
flash[:notice] = "Message failed to save."
format.html { redirect_to posts_path }
end
end
end
end
and corresponding to this I have written the following test case :-
require 'spec_helper'
describe PostsController do
describe "GET 'index'" do
it "returns http success" do
get 'index'
response.should be_success
end
end
describe "#create" do
it "creates a successful mesaage post" do
#post = Post.create(message: "Message")
#post.should be_an_instance_of Post
end
end
end
I am getting failures on both. Please take a look on the code and help me figure out.
I suspect you are not logged in since you are using Devise?
Maybe you need to include the devise testhelpers:
describe PostsController do
include Devise::TestHelpers
before(:each) do
#user = User.create(...)
sign_in #user
end
#assertions go here
end
As Tigraine states, it appears as though you probably are not logged in (with Devise) when the tests get executed. However, showing the failures would help in narrowing down the problem further.
On top of that, the second test isn't really an integration test and I would probably prefer something like the following to test the same condition. There are two types of test you could do:
# inside 'describe "#create"'
let(:valid_params) { {'post' => {'title' => 'Test Post'} }
it 'creates a new Post' do
expect {
post :create, valid_params
}.to change(Post, :count).by(1)
end
# and / or
it 'assigns a new Post' do
post :create, valid_params
assigns(:post).should be_a(Post)
assigns(:post).should be_persisted
end
Don't forget to add this line into your spec_helper.rb
require "devise/test_helpers"
include Devise::TestHelpers
Nevertheless, here is link for Devise wiki - How to test Controllers where you can find more info about this approach. I recommend writing the before method without (:each), what I remember it sometimes causes problems.
before do
#user = FactoryGirl.create(:user)
sign_in #user
end
Can always use:
puts response.inspect
To see how your response looks like.

Testing Rails controllers with RSpec–How to test: current_account.projects

I'm using Rspec and Rails 3 for testing. I've tested my models and helpers but I'm lost on how to begin testing controllers. Almost all of my data in my controller actions is pulled using something like these examples:
#services = current_account.services
#projects = current_person.projects
#projects = current_account.projects.active
# this is really #projects = current_person.account.projects.active)
I can't seem to find any examples of how to test data that's pulled this way. All of the examples I've found aren't scoped to an account or person. Can anyone point me to an article on how to mock or stub this type of arrangement? Is this a sign that this entire approach isn't correct? Below, I've included an entire sample controller.
Any help would be greatly appreciated.
Thanks,
David
class ServicesController < ApplicationController
# Run authorizations
filter_resource_access
# Respond to ...
respond_to :html, :xml, :json
respond_to :js, :only => [:new, :create, :edit, :update, :destroy]
# GET /services
# GET /services.xml
def index
#services = current_account.services.order("name").paginate(:page => params[:page])
respond_with(#services)
end
# GET /services/1
# GET /services/1.xml
def show
#service = current_account.services.find(params[:id])
respond_with(#service)
end
# GET /services/new
# GET /services/new.xml
def new
#service = current_account.services.new
respond_with(#service)
end
# GET /services/1/edit
def edit
#service = current_account.services.find(params[:id])
respond_with(#service)
end
# POST /services
# POST /services.xml
def create
#service = current_account.services.new(params[:service])
if #service.save
# flash[:notice] = 'A service was successfully created.'
end
respond_with(#service, :location => services_url)
end
# PUT /services/1
# PUT /services/1.xml
def update
#service = current_account.services.find(params[:id])
if #service.update_attributes(params[:service])
# flash[:notice] = 'The service was successfully updated.'
end
respond_with(#service, :location => services_url)
end
# DELETE /services/1
# DELETE /services/1.xml
def destroy
#service = current_account.services.find(params[:id])
if #service.destroy
flash[:notice] = "The service was successfully deleted."
else
flash[:warning] = #service.errors.full_messages.inject("") { |acc, message| acc += message }
end
respond_with(#service)
end
end
–––––– UPDATE
Thanks to Xaid's solution I was able to get a solution:
context "logged_in" do
before(:each) do
#current_account = Factory.create(:account)
controller.stub!(:current_account).and_return(#current_account)
#services = FactoryGirl.create_list(:service, 10, :account => #current_account)
#services << #current_account.services.first
#current_account.services.stub!(:all).and_return(#services)
end
# INDEX
describe "GET services" do
before(:each) do
get :index
end
it "should set #services when accessing GET /index" do
assigns[:services].should == #services
end
it "should respond with success" do
response.should be_success
end
end
end
Can't you use something like this to test your 'index' action
describe "GET 'index'" do
before(:each) do
#user = FactoryGirl.create(:user)
controller.stub!(:current_user).and_return(#user)
#services = FactoryGirl.create_list(:service, 10, :user => #user)
#user.services.stub!(:all).and_return(#services)
end
it "should return a list of services" do
get :index
assigns(:services).should == #services
end
end
If I understood your question correctly, you should be able to stub current_user.services(or projects) and make it return some known value (generated by FactoryGirl in my example) and check that against the value thats stored in your action (for example, #services in your 'index' action).

Resources