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
Related
I need to create RSpec testing for the following ruby code and seem to run into issues every time I try. I would love an example or two of RSpec tests that could be created for the following code/methods which are in my controller:
def edit
#movie = Movie.find params[:id]
end
def update
#movie = Movie.find params[:id]
#movie.update_attributes!(movie_params)
flash[:notice] = "#{#movie.title} was successfully updated."
redirect_to movie_path(#movie)
end
def destroy
#movie = Movie.find(params[:id])
#movie.destroy
flash[:notice] = "Movie '#{#movie.title}' deleted."
redirect_to movies_path
end
def find_with_same_director
#movie = Movie.find(params[:id])
#movies, check_info = Movie.find_with_same_director(params[:id])
if check_info
flash[:notice] = "'#{#movie.title}' has no director info"
redirect_to movies_path
end
end
I have this so far:
RSpec.describe MoviesController, type: :controller do
it 'should get all movies in the database' do
get :index
expect(response).to render_template :index
expect(assigns[:movies]).to eq(Movie.all)
end
describe 'find_with_same_director' do
it 'should call the find_with_same_director model method' do
expect(Movie).to receive(:find_with_same_director).with(no_args)
get :find_with_same_director, id: movie.id
end
end
end
but it is not covering it correctly. Any help would be appreciated, thanks.
RSpec.describe MoviesController, type: :controller do
it 'should get all movies in the database' do
get :index
expect(response).to render_template :index
expect(assigns[:movies]).to eq(Movie.all)
end
it 'should update movie in the database' do
Factory.girl.create(:movie)
before_movie_count = Movie.count
put :update, id: movie.id
expect(assigns[:movies]).to eq(...) # whatever data is passed
expect(Movie.count).to eq(before_ml_count)
expect(flash[:snack_class]).to eq(:notice.to_s)
expect(flash[:snack_message]).to ends_with('was successfully updated')
expect(response).to have_http_status(:redirect)
end
it 'should delete movie in the database' do
Factory.girl.create(:movie)
before_movie_count = Movie.count
delete :update, id: movie.id
expect(Movie.count).to eq(before_ml_count - 1)
expect(flash[:snack_class]).to eq(:notice.to_s)
expect(flash[:snack_message]).to ends_with('deleted')
expect(response).to have_http_status(:redirect)
end
describe 'find_with_same_director' do
it 'should call the find_with_same_director model method' do
expect(Movie).to receive(:find_with_same_director).with(no_args)
get :find_with_same_director, id: movie.id
end
end
end
#### in factory movie.rb
FactoryGirl.define do
factory :movie do
# assign data to movie attributes here.
end
end
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
In my controller when an user creates a new post, he/she is redirected to the page that contains the newly created post. I'm wanting to create a test in rspec to cover this redirect but am having trouble with it. Specifically, I want to know what to write in the refirst_to argument. Here is the controller code below..
def create
#micropost = Micropost.new(params[:micropost])
respond_to do |format|
if #micropost.save
format.html {redirect_to #micropost}
else
format.html {render action: 'edit'}
end
end
end
Here is the rspec test...
before do
#params = FactoryGirl.build(:micropost)
end
it "redirects to index" do
#clearly #params.id doesn't work. its telling me instead of a redirect im getting a
#200
#response.should redirect_to(#params.id)
end
Assuming that #params will create a valid Micropost (otherwise .save will fail and you'll be rendering :edit)...
it "redirects to index on successful save" do
post :create, :micropost => #params.attributes
response.should be_redirect
response.should redirect_to(assigns[:micropost])
end
it "renders :edit on failed save" do
post :create, :micropost => {}
response.should render ... # i don't recall the exact syntax...
end
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.
I am having difficulty getting a rspec test for a controller to pass. I would like to test that the POST create action works. I am using rails (3.0.3), cancan (1.4.1), devise (1.1.5), rspec (2.3.0)
The model is dead simple
class Account < ActiveRecord::Base
attr_accessible :name
end
The controller is standard as well (straight out of scaffolding)
class AccountsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :index]
load_and_authorize_resource
...
def create
#account = Account.new(params[:account])
respond_to do |format|
if #account.save
format.html { redirect_to(#account, :notice => 'Account was successfully created.') }
format.xml { render :xml => #account, :status => :created, :location => #account }
else
format.html { render :action => "new" }
format.xml { render :xml => #account.errors, :status => :unprocessable_entity }
end
end
end
and the rspec test I would like to pass is (excuse the title, perhaps not the most appropriate one)
it "should call create on account when POST create is called" do
#user = Factory.create(:user)
#user.admin = true
#user.save
sign_in #user #this is an admin
post :create, :account => {"name" => "Jimmy Johnes"}
response.should be_success
sign_out #user
end
Yet all I get is
AccountsController get index should call create on account when POST create is called
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/accounts_controller_spec.rb:46
Other actions can be tested and do pass (i.e. GET new)
here is the test for GET new
it "should allow logged in admin to call new on account controller" do
#user = Factory.create(:user)
#user.admin=true
#user.save
sign_in #user #this is an admin
get :new
response.should be_success
sign_out #user
end
and for completion here is the ability file
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
Any ideas? My guess is that I am using the wrong rspec expectation, since the code does work (it is just that the test does not perform as desired!)
response.should be_success returns true if the response code is in the range 200-299. But the create action redirects, so the response code gets set to 302, thus the failure.
You can test this by using response.should redirect_to. Check the output of the standard RSpec controller generator for an example, which might look like this:
it "redirects to the created account" do
Account.stub(:new) { mock_account(:save => true) }
post :create, :account => {}
response.should redirect_to(account_url(mock_account))
end
The rspec test that got the test to pass was (thanks to zetetic's advice):
it "should call create on account when POST create is called" do
#user = Factory.create(:user)
#user.admin = true
#user.save
sign_in #user #this is an admin
account = mock_model(Account, :attributes= => true, :save => true)
Account.stub(:new) { account }
post :create, :account => {}
response.should redirect_to(account_path(account))
sign_out #user
end