Destroy an object only by its user creator in Rspec - ruby-on-rails

I have updated the code spec code.
What I seek is to destroy a record only with the same user that has created it.
I've tried in the view section and it seems to be working, but the Rspec is throwing me some errors.
Can anyone please tell me how to do a correct spec?
Thanks
My Record model:
class Record < ActiveRecord::Base
#Associations
belongs_to :user
# Validations
validates :user, presence: true
end
My Record factory:
FactoryGirl.define do
factory :record do
user
end
end
My Record controller:
class RecordsController < ApplicationController
before_action :find_record, only: [:show, :edit, :update, :destroy]
before_action :require_permission, only: [:destroy]
def destroy
#record.destroy
flash[:notice] = "The record was deleted successfully"
redirect_to #record
end
private
def require_permission
if current_user != Record.find(params[:id]).user
flash[:notice] = "Permission required"
redirect_to root_path
end
end
end
My record spec:
require 'rails_helper'
describe RecordsController do
let(:record) { create(:record) }
let(:user) { create(:user) }
describe "#destroy" do
let!(:record) { create(:record) }
#UPDATED
login_user
it "deletes the record" do
expect {
delete :destroy, id: record.id, :record => {:user => record.user}
}.to change(Record, :count).by(-1)
expect(flash[:notice]).to eq("The record was deleted successfully")
end
end
end
UPDATE 2
rails_helper.rb
require 'spec_helper'
require 'devise'
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, type: :controller
end
at spec/support/controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryGirl.create(:admin) # Using factory girl as an example
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
#user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
end
My errors:
#destroy
deletes the record (FAILED - 1)
Failures:
1) RecordsController#destroy deletes the record
Failure/Error: expect {
expected #count to have changed by -1, but was changed by 0

you use #record but you let record that's why in error #record is nil
it "deletes the record" do
expect {
delete :destroy, id: record.id, :record => {:user => record.user}
}.to change(Record, :count).by(-1)
expect(flash[:notice]).to eq("The record was deleted successfully")
end

Related

rails+rspec controller spec with polymorphic association

I'm trying to test the actions in controller spec but for some reason I get the no routes matches error. What should I do to make the route work?
ActionController::UrlGenerationError:
No route matches {:action=>"create", :comment=>{:body=>"Consectetur quo accusamus ea.",
:commentable=>"4"}, :controller=>"comments", :post_id=>"4"}
model
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true, touch: true
class Post < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
routes
resources :posts do
resources :comments, only: [:create, :update, :destroy], module: :posts
end
controller_spec
describe "POST create" do
let!(:user) { create(:user) }
let!(:profile) { create(:profile, user: #user) }
let!(:commentable) { create(:post, user: #user) }
context "with valid attributes" do
subject(:create_action) { xhr :post, :create, post_id: commentable, comment: attributes_for(:comment, commentable: commentable, user: #user) }
it "saves the new task in the db" do
expect{ create_action }.to change{ Comment.count }.by(1)
end
...
EDIT
The controller_spec from above can be found in spec/controllers/comments_controller_spec.rb
controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
#comment = #commentable.comments.new(comment_params)
authorize #comment
#comment.user = current_user
if #comment.save
#comment.send_comment_creation_notification(#commentable)
respond_to :js
end
end
controllers/posts/comments_controller.rb
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
#commentable = Post.find(params[:post_id])
end
Using the module: :posts will route to Posts::CommentsController#create.
If that is not what you intended than remove the module option.
Otherwise you need to ensure that you have the correct class name for both your controller and spec.
class Posts::CommentsController
def create
end
end
RSpec.describe Posts::CommentsController do
# ...
end
Also note that if often does not make sense to nest the "individual actions" for a resource.
Instead you may want to declare the routes like so:
resources :comments, only: [:update, :destroy] # show, edit ...
resources :posts do
resources :comments, only: [:create], module: :posts # new, index
end
Which gives you:
class CommentsController < ApplicationController
before_action :set_posts
# DELETE /comments/:id
def destroy
# ...
end
# PUT|PATCH /comments/:id
def update
end
end
class Posts::CommentsController < ApplicationController
# POST /posts/:post_id/comments
def create
# ...
end
end
See Avoid Deeply Nested Routes in Rails for a deeper explaination of why.
Setting the up the controller to use inheritance in this case is a good idea - however you cannot test the create method through the parent CommentsController class in a controller spec since RSpec will always look at described_class when trying to resolve the route.
Instead you may want to use shared examples:
# /spec/support/shared_examples/comments.rb
RSpec.shared_examples "nested comments controller" do |parameter|
describe "POST create" do
let!(:user) { create(:user) }
context "with valid attributes" do
subject(:create_action) { xhr :post, :create, post_id: commentable, comment: attributes_for(:comment, commentable: commentable, user: #user) }
it "saves the new task in the db" do
expect{ create_action }.to change{ Comment.count }.by(1)
end
end
end
end
require 'rails_helper'
require 'shared_examples/comments'
RSpec.describe Posts::CommentsController
# ...
include_examples "nested comments controller" do
let(:commentable) { create(:post, ...) }
end
end
require 'rails_helper'
require 'shared_examples/comments'
RSpec.describe Products::CommentsController
# ...
include_examples "nested comments controller" do
let(:commentable) { create(:product, ...) }
end
end
The other alternative which I prefer is to use request specs instead:
require 'rails_helper'
RSpec.describe "Comments", type: :request do
RSpec.shared_example "has nested comments" do
let(:path) { polymorphic_path(commentable) + "/comments" }
let(:params) { attributes_for(:comment) }
describe "POST create" do
expect do
xhr :post, path, params
end.to change(commentable.comments, :count).by(1)
end
end
context "Posts" do
include_examples "has nested comments" do
let(:commentable) { create(:post) }
end
end
context "Products" do
include_examples "has nested comments" do
let(:commentable) { create(:product) }
end
end
end
Since you are really sending a HTTP request instead of faking it they cover more of the application stack. This does however come with a small price in terms of test speed. Both shared_context and shared_examples are two of the things which make RSpec really awesome.

how to handle undefined methods during minitest controller testing

I have following methods in my contest model.
class Contest < ActiveRecord::Base
has_many :submissions
has_many :users, through: :submissions
validates_presence_of :name, :admin_id
acts_as_votable
def admin_name
User.find_by_id(self.admin_id).username
end
def tonnage
self.submissions.sum(:tonnage)
end
def contest_type_tr
I18n.t("contests.contest_type")[self.contest_type]
end
def contest_short_descr
I18n.t("contests.contest_short_descr")[self.contest_type]
end
end
When doing a test for the contest controller I get the following error:
ActionView::Template::Error: undefined method `username' for nil:NilClass
Why is this and how can I fix it?
My specs (minitest) are available below.
require "test_helper"
describe ContestsController do
let(:user) { users :default }
let(:contest) { contests :one }
it "gets index" do
get :index
value(response).must_be :success?
value(assigns(:contests)).wont_be :nil?
end
it "gets new" do
get :new
value(response).must_be :success?
end
it "creates contest" do
expect {
post :create, contest: { }
}.must_change "Contest.count"
must_redirect_to contest_path(assigns(:contest))
end
it "shows contest" do
get :show, id: contest
value(response).must_be :success?
end
it "gets edit" do
get :edit, id: contest
value(response).must_be :success?
end
it "updates contest" do
put :update, id: contest, contest: { }
must_redirect_to contest_path(assigns(:contest))
end
it "destroys contest" do
expect {
delete :destroy, id: contest
}.must_change "Contest.count", -1
must_redirect_to contests_path
end
end

NoMethodError: undefined method 'user' for nil:NilClass

I'm working through the book APIs on Rails and am super stuck in chapter 5 trying to test the sessions controller. I'm getting the following error and can't seem to track it down. Is there a good method for hunting down these kinds of error? And what am I missing?
1) Api::V1::SessionsController POST #create when the credentials are correct returns the user record corresponding to the given credentials
Failure/Error: post :create, { session: credentials }
NoMethodError:
undefined method `user' for nil:NilClass
App is in Rails 4.0.2, Ruby 2.2.1
Here is my test:
require 'spec_helper'
describe Api::V1::SessionsController do
describe "POST #create" do
before(:each) do
#user = FactoryGirl.create :user
end
context "when the credentials are correct" do
puts #user
before(:each) do
credentials = { email: #user.email, password: "12345678" }
post :create, { session: credentials }
end
it "returns the user record corresponding to the given credentials" do
#user.reload
expect(json_response[:auth_token]).to eql #user.auth_token
end
it { should respond_with 200 }
end
end
end
Here is the Sessions Controller:
class Api::V1::SessionsController < ApplicationController
respond_to :json
def create
user_password = params[:session][:password]
user_email = params[:session][:email]
user = user_email.present? && User.find_by(email: user_email)
if user.valid_password? user_password
sign_in user, store: false
user.generate_authentication_token!
user.save
render json: user, status: 200, location: [:api, user]
else
render json: { errors: "Invalid email or password" }, status: 422
end
end
end
The User Controller:
class Api::V1::UsersController < ApplicationController
respond_to :json
def show
respond_with User.find(params[:id])
end
def create
user = User.new(user_params)
if user.save
render json: user, status: 201, location: [:api, user]
else
render json: { errors: user.errors }, status: 422
end
end
def update
user = User.find(params[:id])
if user.update(user_params)
render json: user, status: 200, location: [:api, user]
else
render json: { errors: user.errors }, status: 422
end
end
def destroy
user = User.find(params[:id])
user.destroy
head 204
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
The routes.rb:
require 'api_constraints'
MarketPlaceApi::Application.routes.draw do
mount SabisuRails::Engine => "/sabisu_rails"
devise_for :users
# Api definition
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, :only => [:show, :create, :update, :destroy]
resources :sessions, :only => [:create, :destroy]
end
end
end
And the user model:
class User < ActiveRecord::Base
validates :auth_token, uniqueness: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_create :generate_authentication_token!
def generate_authentication_token!
begin
self.auth_token = Devise.friendly_token
end while self.class.exists?(auth_token: auth_token)
end
end
Have you added devise_helper to spec/rails_helper.rb ?
RSpec.configure do |config|
...
config.include Devise::TestHelpers, type: :controller
...
end
Change
if user.valid_password? user_password
to:
if user and valid_password? user_password
or:
if user && valid_password?(user_password)
in your sessions_controller.rb file.
Do you have a User factory?
FactoryGirl.define do
factory :user do
username user
email user#user.com
password '12345678'
password_confirmation { '12345678' }
end
end
You need to have it defined so that FactoryGirl can create a user

CanCan in RSpec Controller spec

I spent most of the day trying to root out a problem with a controller spec, and the current workaround seems unacceptable to me. Any take on why this works? ... and what I should do instead.
Given a simple hierarchy as follows, and the following ability.rb, the properties_controller_spec.rb does not allow the spec below to pass without the line saying:
ability = Ability.new(subject.current_user)
Can you tell me why this would be?
Thanks!
Models:
class Account < ActiveRecord::Base
has_many :properties, :dependent => :nullify
end
class Property < ActiveRecord::Base
belongs_to :account
end
class User < Refinery::Core::BaseModel #for RefineryCMS integration
belongs_to :account
end
Ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :user
can [:read, :create, :update, :destroy], Property, account_id: user.account_id
else
can [:show], Property
end
end
end
properties_contoller_spec.rb:
require 'spec_helper'
describe PropertiesController do
def valid_attributes
describe "Authenticated as Property user" do
describe "PUT update" do
describe "with invalid params" do
it "re-renders the 'edit' template" do
property = FactoryGirl.create(:property, account: property_user.account)
# Trigger the behavior that occurs when invalid params are submitted
Property.any_instance.stub(:save).and_return(false)
ability = Ability.new(subject.current_user) # seriously?
put :update, {:id => property.to_param, :property => { }}, {}
response.should render_template("edit")
end
end
end
end
end
Arg! Found it myself.
Here it is:
config.include Devise::TestHelpers, :type => :controller
Following is the code to sign in the property_user, as directed by the Devise docs. (The locals in question are created in a global_variables.rb that is included. These are used all over the place.)
def signed_in_as_a_property_user
property_user.add_role "User"
sign_in property_user
end
def sign_in_as_a_property_user
property_user.add_role 'User'
post_via_redirect user_session_path,
'user[email]' => property_user.email,
'user[password]' => property_user.password
end

Rspec factory girl issue

I have an rspec/factory girl test where I can't get a test to pass.
I'm using devise where current_user calls the currently logged in User model.
I can load up a test console and type in
u = Factory(:user)
u.company
And this will return a valid company but for some reason in rspec calling current_user.company is returning nil.
Any ideas?
Controller
class CompaniesController < ApplicationController
before_filter :authenticate_user!
def show
#company = current_user.company
end
end
Model
class User < ActiveRecord::Base
validates_uniqueness_of :email, :case_sensitive => false
has_one :company
end
Factory
Factory.define :company do |f|
f.name 'Test Company'
end
Factory.sequence(:email) do |n|
"person#{n}#example.com"
end
Factory.define :user do |f|
f.name 'Test User'
f.email {Factory.next :email}
f.password 'password'
f.company Factory(:company)
end
Test
describe CompaniesController do
before(:each) do
#user = Factory(:user)
sign_in #user
end
describe "GET show" do
before do
get :show
end
it "should find the users company" do
assigns(:company).should be_a(Company)
end
end
end
Spec Helper
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.infer_base_class_for_anonymous_controllers = false
end
Test Result
Failures:
1) CompaniesController GET show should find the users company
Failure/Error: assigns(:company).should be_a(Company)
expected nil to be a kind of Company(id: integer, name: string, user_id: integer, created_at: datetime, updated_at: datetime)
# ./spec/controllers/companies_controller_spec.rb:21:in `block (3 levels) in <top (required)>'
EDIT
I have removed the f.company = Factory(:company) in the factories file. And made my controller spec this
require 'spec_helper'
describe CompaniesController do
let(:current_user) { Factory(:user) }
before(:each) do
sign_in current_user
current_user.company = Factory(:company)
current_user.save
end
describe "GET show" do
before do
get :show
end
it "should find the users company" do
current_user.should respond_to(:company)
assigns(:company).should == current_user.company
end
end
end
I'm not sure but I believe assigns(:company) checks for an instance variable #company which obviously doesn't exist. Try putting #company = #user.company in your before(:each) block or test for it in another way, for example;
it "should find the users company" do
#user.should respond_to(:company)
end
I believe that should do it!
Define Let object for company in your controller rspec.
describe CompaniesController do
describe "authorizations" do
before(:each) do
let(:company) { Factory :company }
let(:user_admin) { Factory(:user) }
end
it "should redirect" do
sign_in(user_admin)
get :show
end
it "should find the users company" do
assigns(:company).should be_a(company)
end
end
end
Can you try with above spec ?
I think the main thing you were missing is setting up an association in your factory. Starting from your original example:
Factory.define :user do |f|
f.name 'Test User'
f.email {Factory.next :email}
f.password 'password'
f.association :company, factory => :company
end
Then when you create a user, it will create a company and fill in user.company_id with the proper id.
See "Associations" in the Factory Girl Getting Started doc.

Resources