I'm writing an app using RoR, using gem Devise for user authentication. I'm trying to test user behaviour when he signed in the app and have next error:
User::TransactionsController when logged in when its own record GET #show assigns the requested instance as #instance
Failure/Error: let(:transaction) { FactoryGirl.create(:transaction, user_id: user.id) }
NameError:
undefined local variable or method `user' for #<RSpec::ExampleGroups::UserTransactionsController::WhenLoggedIn::WhenItsOwnRecord::GETShow:0x00000004d77220>
My tests start with:
RSpec.describe User::TransactionsController, type: :controller do
render_views
before { sign_in FactoryGirl.create :user }
let(:transaction_category) { FactoryGirl.create(:transaction_category) }
let(:transaction) { FactoryGirl.create(:transaction, user_id: user.id) }
......
end
My factory:
FactoryGirl.define do
factory :transaction do
date '2016-01-08'
comment 'MyString'
amount 1
transaction_category
trait :invalid do
amount nil
end
end
end
My TransactionsController looks like:
class User::TransactionsController < ApplicationController
before_action :authenticate_user!
before_action :find_transaction, only: [:show, :edit, :destroy, :update]
def new
#transaction = current_user.transactions.build
end
def show
end
def create
#transaction = current_user.transactions.build(transaction_params)
if #transaction.save
redirect_to user_transaction_url(#transaction)
else
render :new
end
end
def index
#transactions = current_user.transactions
end
def edit
end
def destroy
#transaction.destroy
redirect_to user_transactions_url
end
def update
if #transaction.update(transaction_params)
redirect_to user_transaction_url
else
render :edit
end
end
private
def transaction_params
params.require(:transaction).permit(:amount, :date, :comment,
:transaction_category_id)
end
def find_transaction
#transaction = current_user.transactions.find(params[:id])
end
end
Thanks!
You need to define user
RSpec.describe User::TransactionsController, type: :controller do
render_views
user = FactoryGirl.create( :user )
before { sign_in( user ) }
let(:transaction_category) { FactoryGirl.create(:transaction_category) }
let(:transaction) { FactoryGirl.create(:transaction, user_id: user.id) }
......
end
Related
I want to run the rspec of Tickets controllers, I Have used scaffold and I have made some changes according to my project.
but I am getting some errors. I also have a login (using Devise).
How do I go ahead?
This is the controller-
class TicketsController < ApplicationController
before_action :set_ticket, only: %i[ show edit update destroy assign]
before_action :set_assign_ticket, only: %i[assignid]
# shows the landing page of employee
def landing
#tickets = current_user.employee.tickets.order('tstatus_id, id desc')
end
# Service engineer index showing the tickets which are assinged to him
def slanding
#tickets = Ticket.where(service_id: current_user.employee.service_id).order('tstatus_id, id desc')
end
# Showing tickets list based on the user type
def index
#tickets = if current_user.employee.designation.role == "ADMIN"
Ticket.all.order('tstatus_id ,id desc ')
else
Ticket.where(employee: current_user.employee)
end
end
def show
#tickets = Ticket.all
end
def new
#ticket = Ticket.new
end
#creating a new ticket
def create
#ticket = Ticket.new(ticket_params)
#ticket.employee = current_user.employee
respond_to do |format|
if #ticket.save
format.html { redirect_to landing_path}
else
format.html { render :new, status: :unprocessable_entity }
end
end
end
# updating a ticket
def update
respond_to do |format|
if #ticket.update(ticket_params)
if current_user.employee.designation.role == "ADMIN"
format.html { redirect_to tickets_url }
else
format.html { redirect_to slanding_path }
end
else
format.html { render :edit, status: :unprocessable_entity }
end
end
end
private
def set_ticket
#ticket = Ticket.where(id: params[:id]).first
end
def ticket_params
params.require(:ticket).permit(:kind, :description, :dev_id, :service_id,:tstatus_id)
end
def set_assign_ticket
#ticket = Ticket.find(params[:id])
end
end
This is the model -
class Ticket < ApplicationRecord
belongs_to :service, optional: true
belongs_to :employee
belongs_to :tstatus
before_validation(on: :create) do
self.service_id = 4 # this will assign the service engineer to NONE
self.tstatus_id = 1 # this will assign the status to logged
end
validates :description, presence: true
end
My test cases are:
require 'rails_helper'
describe TicketsController, type: :controller do
# describe 'GET show' do
it 'gets show based on ID' do
#ticket = Ticket.new(id: 1,kind: "Example Ticket", description: "Ticket#example.com", service_id: 1, employee_id: 1,tstatus_id:1)
get :show, params: { id: #ticket.id }
expect(response.status).to eq(200)
end
# describe 'GET edit' do
it 'has 200 status code' do
get :new
expect(response.status).to eq(200)
end
describe 'POST create' do
it 'has 200 status code' do
mock = double('Employee')
expect(mock).to receive(:employee_id)
post :create, params: {
ticket: {
id: 1,kind: "Example Ticket", description: "Ticket#example.com", service_id: 1, employee_id: 1,tstatus_id:1
}
}
expect(response.status).to eq 302
end
end
end
I am a newbie to rails,
Can someone tell me how to write test cases for the controller above?
The workflow should be users should be able to attach files to comments on zendesk ticket regardless if it's a new ticket or existing ticket.
Currently attaching files to comments on existing tickets works perfectly fine. However, if you look at the create action is where it does nothing as far as being able to attach the file. It doesn't throw an error but just doesn't attach the file. I'm sure it's the way my code is flowing but can't figure it out.
require_dependency 'zen_support/application_controller'
module ZenSupport
class TicketsController < ApplicationController
include SmartListing::Helper::ControllerExtensions
helper SmartListing::Helper
before_action :authenticate_user!, except: :zen_update
before_action :set_new_ticket, only: [:new, :create]
before_action :get_ticket, only: [:show, :edit, :update, :zen_update]
before_action :authorize_ticket, except: :zen_update
rescue_from ZendeskAPI::Error::NetworkError, with: :network_error
def new
respond_to do |format|
format.js
end
end
def index
if zen_list_type == 'datatable'
#tickets = tickets
else
smart_listing
end
respond_to do |format|
format.html
format.js
end
end
def create
if(!#ticket.valid? && (#ticket.errors.keys - [:zendesk_id]).present?)
# test if ticket is invalid beyond just missing zendesk_id
raise ActiveRecord::RecordInvalid.new #ticket
else
ActiveRecord::Base.transaction do
#zendesk_ticket.save!
upload_to_zendesk(params[:comment], params[:attachments])
#ticket.zendesk_id = #zendesk_ticket.id
#ticket.status = #zendesk_ticket.status
#ticket.save!
smart_listing
flash.now[:success] = "Your help request was successfully submitted.\n
Your support ticket number is ##{#zendesk_ticket.id}.\n\n
You should receive a confirmation email shortly.\n\n
Thank you!"
end
end
rescue Exception => exception
flash.now[:error] = exception.to_s
#zendesk_ticket.delete if #zendesk_ticket.id
render :new
end
def show
render action: :edit
end
def update
respond_to do |format|
format.js do
# don't save over comment, just use the field for validation
upload_to_zendesk(params[:comment], params[:attachments])
#zendesk_ticket.update(ticket_update_params)
if #ticket.valid? && #zendesk_ticket.save! && #ticket.update(updated_at: Time.now)
flash.now[:success] = "Your help request was successfully updated.\n
Your support ticket number is ##{#zendesk_ticket.id}.\n\n
You should receive a confirmation email shortly.\n\n
Thank you!"
else
flash.now[:error] = if #ticket.errors.present?
#ticket.errors.full_messages.join "\n"
else
'There was a problem sending out your request.'
end
render action: :edit
end
end
end
end
def zen_update
ZenSupport::Ticket.find_by(
zendesk_id: params[:id]
)&.update(zen_update_params)
render nothing: true
end
def network_error
flash.now[:error] = 'There was a network error connecting to the Zendesk API. Try again later.'
render 'network_error'
end
private
def upload_to_zendesk(comment_body, files)
#zendesk_ticket.comment = { value: comment_body }
Array(files).each do |file|
#zendesk_ticket.comment.uploads << file
end
end
def tickets
ZenSupport::Ticket.where user: current_user
end
def smart_listing
smart_listing_create(
:support_tickets,
tickets,
partial: 'zen_support/tickets/list',
array: tickets.count != 0
# we want to use array: true if there are records present because it pre-renders the activerecord
# objects so we can do sorting on the encrypted field 'subject'. BUT, if there are no records,
# then array: true gives us a pagination error
)
end
def ticket_params
{
subject: subject,
comment: comment,
zendesk_id: zendesk_id,
zendesk_submitter_id: submitter_id,
ticket_type: ticket_type,
priority: priority,
tags: tags,
user: current_user
}
end
def zendesk_ticket_params
{
subject: subject,
type: ticket_type,
comment: { value: comment },
submitter_id: submitter_id,
requester_id: requester_id,
priority: priority,
tags: tags
}
end
def ticket_update_params
{
comment: {
body: params[:comment],
author_id: zendesk_user.id
}
}
end
def zen_update_params
{
zendesk_id: zendesk_id,
status: #zendesk_ticket.status,
priority: #zendesk_ticket.priority,
updated_at: #zendesk_ticket.updated_at
}
end
def subject
params.dig :zen_support_ticket, :subject
end
def comment
params.dig :zen_support_ticket, :comment
end
def zendesk_id
#zendesk_ticket.id
end
def tags
[
*params.dig(:zen_support_ticket, :tags),
*ZenSupport.configuration.options[:support_ticket_attributes][:static_tags]
]
end
def ticket_type
params.dig(:zen_support_ticket, :ticket_type).to_s.downcase
end
def set_new_ticket
#zendesk_ticket = ZendeskAPI::Ticket.new(
zen_client, zendesk_ticket_params
)
#ticket = ZenSupport::Ticket.new(ticket_params)
end
def get_ticket
#zendesk_ticket = ZendeskAPI::Ticket.find!(
zen_client,
id: params[:id]
)
#ticket = ZenSupport::Ticket.find_by!(zendesk_id: params[:id])
end
def priority
(params.dig(:zen_support_ticket, :priority) || ZenSupport.configuration.options[:support_ticket_attributes][:default_priority]).downcase
end
def submitter_id
zendesk_user.id
end
def requester_id
zendesk_user.id
end
def user_params
params.require(:zen_support_user).permit(tags: [])
end
def zendesk_user
#zendesk_user ||= (get_zendesk_user || create_zendesk_user)
end
def get_zendesk_user
zen_client.users.search(
query: "email:#{zen_current_user.email}"
).first
end
def create_zendesk_user
ZendeskAPI::User.create(
zen_client,
email: zen_current_user.email,
name: zen_current_user.full_name,
tags: user_tags
)
end
def user_tags
ZenSupport.configuration.user_setup[:static_tags]
end
def authorize_ticket
authorize Ticket
end
end
end
try adding this one-line to your create-method
def create
if(!#ticket.valid? && (#ticket.errors.keys - [:zendesk_id]).present?)
# test if ticket is invalid beyond just missing zendesk_id
raise ActiveRecord::RecordInvalid.new #ticket
else
ActiveRecord::Base.transaction do
#zendesk_ticket.save!
upload_to_zendesk(params[:comment], params[:attachments])
#zendesk_ticket.save!
...
I have problem with my "POST create" action. Test passed successfully, when attributes are valid, but when they are invalid, player is also saved. It's strange because, only :invalid_player, can be saved with invalid attributes. If I change for example, wins to -1 or "string", player with attributes :invalid_player is saved. But if I change attributes for :player, like wins = -1, validators prevent player, from being saved.
Console output with error message:
Failures:
1) PlayersController user is signed in POST create with invalid attributes does not save the new player
Failure/Error:
expect{
post :create, { tournament_id: #tournament, player: FactoryGirl.attributes_for(:invalid_player) }
}.to_not change(Player, :count)
expected #count not to have changed, but did change from 1 to 2
# ./spec/controllers/players_controller_spec.rb:111:in `block (5 levels) in <top (required)>'
This is my Player model:
class Player < ActiveRecord::Base
belongs_to :user
belongs_to :tournament
validates :wins, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :loses, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :draws, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
end
Factory file for players:
FactoryGirl.define do
factory :player do
wins 0
loses 0
draws 0
end
factory :invalid_player, parent: :player do
wins -1
loses 0
draws 0
end
end
Spec test:
context "user is signed in" do
before do
#tournament = create(:tournament)
#player = create(:player)
#user = create(:user)
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in(#user)
controller.stub(:current_user).and_return(#user)
end
describe "GET new" do
end
describe "GET index" do
it "renders the :index view" do
get :index, tournament_id: #tournament
expect(response).to render_template :index
end
end
describe "GET show" do
it "renders the :show view" do
get :show, { id: #player, tournament_id: #tournament }
expect(response).to render_template :show
end
end
describe "POST create" do
context "with valid attributes" do
it "creates a new player" do
expect{
post :create, { tournament_id: #tournament, player: FactoryGirl.attributes_for(:player) }
}.to change(Player,:count).by(1)
end
it "redirects to the tournament" do
post :create, { tournament_id: #tournament, player: FactoryGirl.attributes_for(:player) }
expect(response).to redirect_to #tournament
end
end
context "with invalid attributes" do
it "does not save the new player" do
expect{
post :create, { tournament_id: #tournament, player: FactoryGirl.attributes_for(:invalid_player) }
}.to_not change(Player, :count)
end
it 're-renders the new method' do
post :create, { tournament_id: #tournament, player: FactoryGirl.attributes_for(:invalid_player) }
response.should render_template :new
end
end
end
end
Controller:
class PlayersController < ApplicationController
before_action :set_tournament
before_action :set_admin, only: [:edit, :update, :destroy]
before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
def index
#players = #tournament.players.all
end
def show
#player = Player.find(params[:id])
end
def new
#player = #tournament.players.new
end
def create
if current_user.player.nil? == false
flash[:error] = "You're already in tournament."
redirect_to tournaments_url
else
#player = #tournament.players.new
#player.user_id = current_user.id
if #player.save
redirect_to #tournament
else
render 'new'
end
end
end
def edit
if current_user == #admin
#player = #tournament.players.find(params[:id])
else
redirect_to tournaments_url
end
end
def update
if current_user == #admin
#player = #tournament.players.find(params[:id])
if #player.update_attributes(game_params)
flash[:success] = "Player was updated successful"
redirect_to #tournament
end
else
redirect_to tournaments_url
end
end
def destroy
#player = Player.find(params[:id])
flash[:success] = "Player deleted"
redirect_to #tournament
end
private
def set_tournament
#tournament = Tournament.find(params[:tournament_id])
end
def set_admin
#tournament = Tournament.find(params[:tournament_id])
#admin = #tournament.user
end
end
You are not assigning any attributes to your model in your create method. You need to do the following (I assume it's rails 4):
#player = #tournament.players.new(player_params)
#...
private
def player_params
params.require(:player).permit(:wins, :loses, :draws)
end
Without any assignment you most likely falling back onto database default value of zero, which is valid.
I'm having problems to pass a test in rails with rspec. This is what console tells me when I ran the tests.
The fail is ControlsController GET index logged in renders the index template
Failure/Error: expect(response). to render_template(:index)
expecting <"index"> but rendering with <[]>
And this is my code
require "rails_helper"
RSpec.describe ControlsController, :type => :controller do
render_views
describe "GET index" do
let(:user) {
FactoryGirl.create(:user)
}
let(:control) {
FactoryGirl.create(:control, user: user)
}
context "logged in" do
before :each do
sign_in :user, user
end
it "loads all controls into #controls" do
get :index, { user_id: user.id}
expect(assigns(:controls)).to eq([control])
end
it "assigns a new control to #control" do
get :index, { user_id: user.id}
expect(assigns(:control)).to be_a_new(Control)
end
it "renders the index template" do
get :index, { user_id: user.id}
expect(response). to render_template(:index)
end
it "a user can't see the controls from other user" do
new_user = User.create(name: "Juan",
email: "juan#gmail.com",
password: "123456789",
password_confirmation: "123456789")
get :index, { user_id: new_user.id}
expect(response).to redirect_to root_path
end
class ControlsController < ApplicationController
before_action :authenticate_user!
def index
#user= current_user
#control= Control.new
# #control_last = Control.lastcontrol (current_user.id)
# #controls_average = Control.controls_average (current_user.id)
# #controls_average_day = Control.controls_day_average (current_user.id)
#controls = Control.all
if params[:user_id] != current_user.id
redirect_to root_path
end
end
The answer is to make a private method and redirect_to user_controls_path current_user.name
This is the new code of the controller
controlsController.rb
class ControlsController < ApplicationController
before_action :authenticate_user!
before_action :redirect_if_not_current_user, only: :index
private
def control_params
params.require(:control).permit(:level, :period, :day)
end
def redirect_if_not_current_user
if params[:user_id] != current_user.name
redirect_to user_controls_path current_user.name
end
end
I test my destroy and update methods in hotel_controller and Im keep getting ActiveRecord:RecordNotFound error. Heres a screenshot
I think this is coz FactoryGirs doesnt save recods to the db. Help me pls to get things right.
hotels_controller.rb
class HotelsController < ApplicationController
before_action :signed_in_user, except: [:index, :show, :top5hotels]
...
def destroy
#hotel = current_user.hotels.find(params[:id])
#hotel.destroy
redirect_to hotels_url
end
def update
#hotel = current_user.hotels.find(params[:id])
if #hotel.update_attributes!(params[:hotel])
redirect_to #hotel, notice: "Hotel was successfully updated."
else
render "edit"
end
end
...
end
factories.rb
FactoryGirl.define do
factory :hotel do
name 'NewHotel'
star_rating 5
breakfast false
room_description 'Room Description'
price_for_room 500
user { create(:user) }
address { create(:address) }
end
factory :user do
sequence(:email) { |n| "user_mail.#{n}#gmail.com" }
name 'Yuri Gagarin'
password 'foobar'
password_confirmation 'foobar'
end
factory :rating do
value 5
user { create(:user) }
hotel { create(:hotel) }
end
factory :comment do
body "Heresanytextyouwant"
user { create(:user) }
hotel { create(:hotel) }
end
factory :address do
country 'Country'
state 'State'
city 'City'
street 'Street'
end
end
hotels_controller_spec.rb
require 'spec_helper'
describe HotelsController do
before { sign_in user, no_capybara: true }
...
describe "destroy action" do
it "redirects to index action when hotel is destroyed" do
hotel = create(:hotel)
delete :destroy, id: hotel.id
expect(response).to redirect_to(hotels_url)
end
end
describe "update action" do
it "redirects to the hotel" do
hotel = create(:hotel)
put :update, id: hotel.id, hotel: FactoryGirl.attributes_for(:hotel)
expect(assigns(:hotel)).to be_eq(hotel)
#expect(response).to render_template('show')
end
end
end
FactoryGirl IS saving records to db.
The trouble is the current_user is not the same user that the hotel belongs to, so when you try to retrieve the hotel record it's not found.
Try changing...
#hotel = current_user.hotels.find(params[:id])
to...
#hotel = Hotel.find(params[:id])
And you'll see it works.
If you want to keep the original code, then in the test you should be doing...
hotel = create(:hotel, user: current_user)