I've written some tests for simple application. I'm having problem with #destroy method in my authors_controller. As I've done this from some tutorials (many sources shows similar approach) I guess it should work, but such error occurs:
Failure/Error: expect { delete :destroy, id: author.id }.to change(Author, :count).by(-1) expected #count to have changed by -1, but was changed by 0
Here's my code:
author_controller_spec.rb
require 'rails_helper'
describe AuthorsController do
let(:author) { FactoryGirl.create(:author) }
describe 'DELETE #destroy' do
it 'deletes author' do
expect { delete :destroy, id: author.id }.to change(Author, :count).by(-1)
end
end
end
authors_controller.rb
class AuthorsController < ApplicationController
def show
#author = Author.find(params[:id])
end
def new
#author = Author.new
end
def create
#author = Author.new(author_params)
if #author.save
redirect_to #author
else
render 'new'
end
end
def edit
#author = Author.find(params[:id])
end
def update
#author = Author.find(params[:id])
if #author.update(author_params)
redirect_to #author
else
render 'edit'
end
end
def destroy
#author = Author.find(params[:id])
#author.books.each do |book|
book.destroy if book.authors.count == 1
end
#author.destroy
redirect_to authors_path
end
def index
#author = Author.all
end
private
def author_params
params.require(:author).permit(:name, :surname, book_ids: [])
end
end
The let call is not made until the first time you mention the variable, as it's lazy evaluation. This means that within your expect block, you are both creating and destroying the record, leading to an overall change of 0.
Either create the author outside of the block:
describe AuthorsController do
let(:author) { FactoryGirl.create(:author) }
describe 'DELETE #destroy' do
author
it 'deletes author' do
expect { delete :destroy, id: author.id }.to change(Author, :count).by(-1)
end
end
end
Or, tell the let block not to evaluate lazily by using let!:
describe AuthorsController do
let!(:author) { FactoryGirl.create(:author) }
describe 'DELETE #destroy' do
it 'deletes author' do
expect { delete :destroy, id: author.id }.to change(Author, :count).by(-1)
end
end
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?
I have a problem with my testing. I have used scaffolds to create my app, but changed something in my controllers. My first controller looks as follow
class ChildrenController < ApplicationController
before_action :set_child, only: [:show, :edit, :update, :destroy]
require 'httparty'
# require 'open-uri'
include HTTParty
# include I18n
def child_admin
#children = Child.all
end
# GET /children
# GET /children.json
def index
#children = Child.all
end
# GET /children/1
# GET /children/1.json
def show
#child = Child.find(params[:id])
authorize! :read, #child
end
# GET /children/new
def new
#child = Child.new
end
# GET /children/1/edit
def edit
end
# POST /children
# POST /children.json
def create
#kigas = Kiga.all
#relations = Relation.all
#child = Child.new child_params
#child.user = current_user
url = "https://maps.googleapis.com/maps/api/geocode/json?address=#{I18n.transliterate(#child.city)}+#{I18n.transliterate(#child.streed)}+#{#child.add_number}&key=AIzaSyBWwoVj5WQMN9-Ij7IJWxQL1CzuigzBsYc"
latlongchild = HTTParty.get(url)
# puts latlongchild
# puts latlongchild["results"].first["geometry"]["location"]
childlat=latlongchild["results"].first["geometry"]["location"]["lat"]
childlng=latlongchild["results"].first["geometry"]["location"]["lng"]
# #child.save
respond_to do |format|
if #child.save
format.html { redirect_to #child, notice: 'Kind wurde erfolgreich registriert.' }
format.json { render :show, status: :created, location: #child }
else
format.html { render :new }
format.json { render json: #child.errors, status: :unprocessable_entity }
end
end
#kigas.each do |kiga|
url2 = "https://maps.googleapis.com/maps/api/distancematrix/json?origins=#{I18n.transliterate(#child.city)}+#{I18n.transliterate(#child.streed)}+#{#child.add_number}&destinations=#{I18n.transliterate(kiga.city)}+#{I18n.transliterate(kiga.streed)}+#{kiga.add_number}&key=AIzaSyDjh9hMXm_lnIyaj_HLQpGvDcDasLjyhxk"
response = HTTParty.get(url2)
mesponse=response["rows"].first["elements"].first["distance"]["value"]
url3 = "https://maps.googleapis.com/maps/api/geocode/json?address=#{I18n.transliterate(kiga.city)}+#{I18n.transliterate(kiga.streed)}+#{kiga.add_number}&key=AIzaSyBWwoVj5WQMN9-Ij7IJWxQL1CzuigzBsYc"
response2 = HTTParty.get(url3)
kigalat=response2["results"].first["geometry"]["location"]["lat"]
kigalng=response2["results"].first["geometry"]["location"]["lng"]
# puts mesponse
# puts mysponse
# puts #child.id
# puts kiga.id
#relation = Relation.new relation_params
#relation.child_id = #child.id
#relation.kiga_id = kiga.id
#relation.distance = mesponse
#relation.kigalat = kigalat
#relation.kigalong = kigalng
#relation.childlat = childlat
#relation.childlong = childlng
#relation.user_id = #child.user_id
#relation.save
end
end
# PATCH/PUT /children/1
# PATCH/PUT /children/1.json
def update
respond_to do |format|
if #child.update(child_params)
format.html { redirect_to #child, notice: 'Kind wurde erfolgreich angepasst.' }
format.json { render :show, status: :ok, location: #child }
else
format.html { render :edit }
format.json { render json: #child.errors, status: :unprocessable_entity }
end
end
end
# DELETE /children/1
# DELETE /children/1.json
def destroy
#child.destroy
respond_to do |format|
format.html { redirect_to children_url, notice: 'Kind wurde erfolgreich ausgetragen.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_child
#child = Child.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def child_params
params.require(:child).permit(:name, :city, :postalcode, :streed, :add_number, :disability, :halal, :koscha, :vegetarian, :vegan, :allday, :gender)
end
def set_relation
#relation = Relation.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def relation_params
# params.require(:relation).permit(:kiga_id, :child_id, :assignment, :preference, :distance)
end
end
and my controller test as follows:
require 'test_helper'
class ChildrenControllerTest < ActionDispatch::IntegrationTest
setup do
#child = children(:one)
end
test "should get index" do
get children_url
assert_response :success
end
test "should get new" do
get new_child_url
assert_response :success
end
test "should create child" do
assert_difference('Child.count') do
post children_url, params: { child: { add_number: #child.add_number, allday: #child.allday, city: #child.city, disability: #child.disability, gender: #child.gender, halal: #child.halal, koscha: #child.koscha, name: #child.name, postalcode: #child.postalcode, streed: #child.streed, vegan: #child.vegan, vegetarian: #child.vegetarian } }
end
assert_redirected_to child_url(Child.last)
end
test "should show child" do
get child_url(#child)
assert_response :success
end
test "should get edit" do
get edit_child_url(#child)
assert_response :success
end
test "should update child" do
patch child_url(#child), params: { child: { add_number: #child.add_number, allday: #child.allday, city: #child.city, disability: #child.disability, gender: #child.gender, halal: #child.halal, koscha: #child.koscha, name: #child.name, postalcode: #child.postalcode, streed: #child.streed, vegan: #child.vegan, vegetarian: #child.vegetarian } }
assert_redirected_to child_url(#child)
end
test "should destroy child" do
assert_difference('Child.count', -1) do
delete child_url(#child)
end
assert_redirected_to children_url
end
end
and I've also defined some abilities:
class Ability
include CanCan::Ability
def initialize(user)
if user.admin?
can :manage, :all
can :view, Child
can :view, Kiga
can :read, Kiga
can :read, Child
can :read, User
elsif user.role == 'Eltern'
can [:update, :destroy], Child do |child|
child.user_id == user.id
end
can :view, Child do |child|
child.user_id == user.id
end
can :read, Child do |child|
child.user_id == user.id
end
can :create, Child
can :read, Kiga
can :results_child, Child
can [:read, :view], Relation do |relation|
relation.user_id == user.id
end
else
end
end
end
I'm usingtest_helper and cancanfor the abilities. I thought my change in the abilities destroyed my testing, because my index, show, create and update test produces errors with the following massage
ActionView::Template::Error: undefined method 'admin?' for nil:NilClass
Can someone help me and can tell me, how I have to rearrange my controller and tests? Or is there another big mistake?
Thank you very much!
The user.admin? call is what's causing the error.
The ability.rb that gets generated by cancancan includes a commented out line as part of the example:
# user ||= User.new # guest user (not logged in)
This is because when you don't have a signed in user nil gets passed in to create the Ability instance.
With user ||= User.new it means that you have an actual, although not saved, user instance to interact with. Putting that line as the first in your initialize method for Ability is probably easier than making all your user checks something like if user && user.admin?.
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.
How can I test this code in my controller? My problem is with the wizard_incompleted? method
class ApplicantsController < ApplicationController
def index
#applicant = current_user.applicant
#application = #applicant.applications.last
if #application.wizard_incompleted?
# some redirect
end
end
end
describe "GET #index" do
let(:application) { double('application')}
it "redirect to wizard if it is incompleted" do
get :index
allow_any_instance_of(application).to receive(:wizard_incompleted?).and_return(true)
expect(response).to redirect_to(new_applicants_application_path)
end
end
You can controller test this
# app/controllers/applicants_controller.rb
class ApplicantsController < ApplicationController
def index
#applicant = current_user.applicant
#application = #applicant.applications.last
redirect_to "/" if #application.wizard_incompleted?
end
end
Like this
# spec/controllers/applicants_controller_spec.rb
require "spec_helper"
describe ApplicantsController, type: :controller do
it "#index" do
last_application = double(:last_application, wizard_incompleted?: true)
applications = double(:applications, last: last_application)
applicant = double(:applicant, applications: applications)
current_user = double(:current_user, applicant: applicant)
expect(controller).to receive(:current_user).and_return(current_user)
expect(current_user).to receive(:applicant).and_return(applicant)
expect(applicant).to receive(:applications).and_return(applications)
expect(applications).to receive(:last).and_return(last_application)
expect(last_application).to receive(:wizard_incompleted?).and_return(true)
get :index
expect(assigns(:applicant)).to eq applicant
expect(assigns(:application)).to eq last_application
expect(response).to redirect_to "/"
end
end
I have a nested resource specialty under user.
My routes.rb looks like
resources :users do
resources :specialties do
end
end
My factories.rb looks like
Factory.define :user do |f|
f.description { Populator.sentences(1..3) }
f.experience { Populator.sentences(1..5) }
f.tag_list { create_tags }
end
Factory.define :specialty do |f|
f.association :user
specialties = CategoryType.valuesForTest
f.sequence(:category) { |i| specialties[i%specialties.length] }
f.description { Populator.sentences(1..5) }
f.rate 150.0
f.position { Populator.sentences(1) }
f.company { Populator.sentences(1) }
f.tag_list { create_tags }
end
My Specialties_controller.rb looks like
class SpecialtiesController < ApplicationController
def index
#user = User.find(params[:user_id])
#specialties = #user.specialties
respond_to do |format|
format.html # index.html.erb
format.json { render json: #specialties }
end
end
My specialties_controller_spec.rb looks like
require 'spec_helper'
describe SpecialtiesController do
render_views
describe "GET 'index'" do
before do
#user = Factory.create(:user)
#specialty = Factory.create(:specialty, :user => #user)
#user.stub!(:specialty).and_return(#specialty)
User.stub!(:find).and_return(#user)
end
def do_get
get :index, :user_id => #user.id
end
it "should render index template" do
do_get
response.should render_template('index')
end
it "should find user with params[:user_id]" do
User.should_receive(:find).with(#user.id.to_s).and_return(#user)
do_get
end
it "should get user's specialties" do
#user.should_receive(:specialty).and_return(#specialty)
do_get
end
end
end
The first two tests pass, but the last test fails with the error message
Failure/Error: #user.should_receive(:specialty).and_return(#specialty)
(#<User:0x007fe4913296a0>).specialty(any args)
expected: 1 time
received: 0 times
Does anybody have an idea what this error means and how to fix it? I've looked at similar posts and can't find an error in my code. Thanks in advance.
#user.should_receive(:specialty).and_return(#specialty)
specialtyis a one-to-many relation and should be plural: specialties. And indeed you have in your controller:
#specialties = #user.specialties