In my small app , users can post comments. These comments can be destroyed only by their owners. I am trying to log in a user, create a comment, log out a user and then try to delete the comment that the first user created. However this action succeds for some reason. This is my comments controllor only showing the create and update actions and private methods:
module Api
module V1
class CommentsController < Api::V1::BaseController
before_action :check_user
before_action :get_comment, only: [:destroy, :update]
respond_to :json
def destroy
if #comment.destroy
head :no_content, status: :no_content
else
render json: serialize_model(#comment.errors)
end
end
def update
if #comment.update_attributes(comment_params)
render json: serialize_model(#comment), status: :accepted
else
render json: { errors: #comment.errors }, status: :bad_request
end
end
private
def comment_params
params.require(:comment).permit(:text, :picture_id)
end
def get_comment
#comment = Comment.find_by_id(params[:id])
check_owner
end
def check_user
render json: { errors: { user: "not signed in" } }, status: :unauthorized unless user_signed_in?
end
def check_owner
render json: { errors: { user: "not the owner" } }, status: :unauthorized unless current_user.id = #comment.id
end
end
end
end
These are my shared exmples for the test:
shared_context 'comments' do
def setup_requirements_without_login
#user = FactoryGirl.create(:user)
#category = FactoryGirl.create(:category)
#picture = FactoryGirl.create(:picture, category_id: #category.id, user_id: #user.id)
end
def setup_requirements_with_login
setup_requirements_without_login
sign_in(#user)
end
shared_examples 'not the owner' do
it 'creates a resource' do
body = JSON.parse(response.body)
expect(body).to include('errors')
data = body['errors']
expect(data).to include('user')
end
it 'responds with 401' do
expect(response).to have_http_status(401)
end
end
end
And these are the tests for update and destroy action:
require "rails_helper"
include Warden::Test::Helpers
Warden.test_mode!
RSpec.describe Api::V1::CommentsController, type: :controller do
include_context 'comments'
describe 'PATCH /api/comments/:id' do
context 'when it is a valid request' do
let(:attr) do
{ text: 'update' }
end
before(:each) do
setup_requirements_with_login
#comment = FactoryGirl.create(:comment, picture_id: #picture.id, user_id: #user.id)
patch :update, id: #comment.id, comment: attr , format: :json
end
it 'creates a resource' do
body = JSON.parse(response.body)
expect(body).to include('data')
data = body['data']
expect(data['attributes']['text']).to eq('update')
end
it 'responds with 202' do
expect(response).to have_http_status(202)
end
end
context 'when the user is not logged in' do
let(:attr) do
{ text: 'update' }
end
before(:each) do
setup_requirements_without_login
#comment = FactoryGirl.create(:comment, picture_id: #picture.id, user_id: #user.id)
patch :update, id: #comment.id, comment: attr , format: :json
end
it_behaves_like "not logged in"
end
context 'when the user is not the owner' do
let(:attr) do
{ text: 'update' }
end
before(:each) do
setup_requirements_with_login
#comment = FactoryGirl.create(:comment, picture_id: #picture.id, user_id: #user.id)
sign_out(#user)
logout
#user2 = FactoryGirl.create(:user)
sign_in(#user2)
patch :update, id: #comment.id, comment: attr , format: :json
end
it_behaves_like "not the owner"
end
end
describe 'DELETE /api/comments/:id' do
context 'when it is a valid request' do
before(:each) do
setup_requirements_with_login
#comment = FactoryGirl.create(:comment, picture_id: #picture.id, user_id: #user.id)
delete :destroy, id: #comment.id, format: :json
end
it 'responds with 204' do
expect(response).to have_http_status(204)
end
end
context 'when the user is not logged in' do
before(:each) do
setup_requirements_without_login
#comment = FactoryGirl.create(:comment, picture_id: #picture.id, user_id: #user.id)
delete :destroy, id: #comment.id, format: :json
end
it_behaves_like "not logged in"
end
context 'when the user is not the owner' do
before(:each) do
setup_requirements_with_login
#comment = FactoryGirl.create(:comment, picture_id: #picture.id, user_id: #user.id)
sign_out(#user)
logout
#user2 = FactoryGirl.create(:user)
sign_in(#user2)
delete :destroy, id: #comment.id, format: :json
end
it_behaves_like "not the owner"
end
end
end
My problem is that the action succeeds when it shouldn't for some reason. I use pry to debugg and it makes me question the tests even more because it says current_user has the id of 97 when the test created users with the ids: 1001 and 1002 which is very odd... . Did I make a mistake in the controller ? or tests?
your check_owner function should have == instead of = in its unless condition:
unless current_user.id == #comment.id
Otherwise the id from the #comment gets assigned to current_user.id. This is probably the origin for your 97. =)
def get_comment
#comment = current_user.comments.find! params[:id]
end
This automatically adds the association to the SQL query (where user_id=1337) and the bang method (with the !) throws an 404 Exception if record wasnt found. That is the easiest way to controll that only the owner has access to its own records.
Related
I want to test comments controller, action create, but I don't know how what's wrong with it. Test are not save comment
comments_controller.rb is work in my projetc, i can see all comments by rails console (as like Comments.all. So that valid:
class CommentsController < ApplicationController
before_action :authenticate_user!, only:[:create,:vote]
before_action :show, only:[:show,:vote]
respond_to :js, :json, :html
def create
#comment = Comment.new(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to post_path(#comment.post.id)
else
redirect_to root_path
end
end
private
def comment_params
params.require(:comment).permit(:comment, :post_id)
end
end
comments_controller_spec.rb is here. It seems like that i send bad params
require 'rails_helper'
RSpec.describe CommentsController, type: :controller do
let(:user) {create :user}
let(:params) { {user_id: user} }
before {sign_in user}
describe '#create' do
let(:post) {create :post}
let(:params) do
{
user_id: user.id,
post_id: post.id,
comment: attributes_for(:comment)
}
end
subject {process :create, method: :post, params: params}
it 'create comment' do
expect{subject}.to change {Comment.count}.by(1)
# is_expected.to redirect_to(user_post_path(assigns(:user), assigns(:post)))
end
end
end
factory comments.rb is here:
FactoryBot.define do
factory :comment do
association :post
association :user
user_id { FFaker::Internet.email }
comment { FFaker::Lorem.sentence }
end
end
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 trying to write spec code for my controller it gets failed. And i am not sure where it gets failed.
Controller Code
def index
#users = User.all
end
def update
authorize! :update, #user
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to user_index_path }
else
format.html { render :index }
end
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.permit(:active)
end
Spec Code for the above controller
RSpec.describe UserController, type: :controller do
describe 'GET #index' do
let(:user) {User.create!(name: "hari")}
context 'with user details'do
it 'loads correct user details' do
get :index
expect(response).to permit(:user)
end
end
context 'without user details' do
it 'doesnot loads correct user details' do
get :index
expect(response).not_to permit(:user)
end
end
end
describe 'Patch #update' do
context 'when valid params' do
let(:attr) do
{active: 'true'}
end
before(:each) do
#user = subject.current_user
put :update, params: { user: attr }
#user.reload
end
it 'redirects to user_index_path ' do
expect(response).redirect_to(user_index_path)
end
it 'sets active state' do
expect(#user.active?('true')).to be true
end
end
context 'when invalid param' do
let(:attr) do
{active: 'nil'}
end
before(:each) do
#user = subject.current_user
put :update, params: { user: attr }
#user.reload
end
it 'render index' do
expect(respone.status).to eq(200)
end
it 'doesnot change active state' do
expect(#user.active?(nil)).to be true
end
end
end
end
I am just a beginner and tried the spec code for my controller by checking https://relishapp.com/rspec/rspec-rails/docs/gettingstarted. Can you help me where my spec goes wrong or could anyone give me a few test examples for these methods or could redirect me to an rspec guide? the index method is getting failed
and my
terminal log is
1) UserController GET #index with user details loads correct user details
Failure/Error: expect(response).to permit(:user)
NoMethodError:
undefined method `permit' for #<RSpec::ExampleGroups::UserController::GETIndex::WithUserDetails:0x00005614152406b0>
Did you mean? print
# ./spec/controllers/user_controller_spec.rb:10:in `block (4 levels) in <top (required)>'
I am learning rspec and trying to test my Account controller on create. After a user creates an account (i.e. chooses a name and a subdomain), he's redirected to a login page on his new subdomain.
My test returns NoMethodError: undefined method 'subdomain' for #<Hash:0x00000107888c88>
My account Factory is setup to generate a subdomain, so I don't see a problem with my logic. Is is just a syntax issue ?
accounts_controller.rb
class AccountsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:new, :create]
def create
#account = Account.new(account_params)
respond_to do |format|
if #account.save
format.html { redirect_to new_user_session_url(subdomain: #account.subdomain, mp: 'signup' ) }
else
format.html { render action: 'new' }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
end
/specs/controlles/accounts_controller_spec.rb
require 'rails_helper'
RSpec.describe AccountsController, :type => :controller do
describe "POST #create" do
context "with valid attributes" do
before :each do
#account = FactoryGirl.attributes_for(:account).merge( owner_attributes: FactoryGirl.attributes_for(:owner) )
end
it "redirects to the account subdomain login page" do
expect(post :create, account: #account).to redirect_to new_user_session_url(:subdomain => #account.subdomain)
end
end
context "with invalid attributes" do
it "does not save the new account in the database"
it "re-renders the :new template"
end
end
end
In your test, #account is a hash of account attributes
#account = FactoryGirl.attributes_for(:account).merge( owner_attributes: FactoryGirl.attributes_for(:owner) )
The above line returns a hash which you are passing as parameter while making the request
you should probably be doing account[:subdomain]
expect(post :create, account: #account).to redirect_to new_user_session_url(:subdomain => #account[:subdomain])
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