Rspec test got: nil - ruby-on-rails

I'm trying to do a basic rspec test, which looks like this;
require 'rails_helper'
describe ReviewsController do
describe "GET #index" do
it "assigns a new review to #reviews" do
review = Review.create( rating: 4 )
get :index
expect(assigns(:review)).to eq([review])
assert_response :success
end
end
end
But I'm getting the failure: expected: Review id: 8, rating: 4, created_at: "2016-07-19 11:58:28", updated_at: "2016-07-19 11:58:28", user_id: nil, game_id: nil
got: nil
My ReviewsController looks like this:
class ReviewsController < ApplicationController
def index
#reviews = Review.all
end
def show
#review = Review.find(params[:rating])
end
def create
review = Review.new(review_params)
respond_to do |format|
if #review.save
format.html { redirect_to root_url, notice: 'Review was successfully updated.' }
format.json { render :show, status: :ok, location: #review }
else
format.html { render :new }
format.json { render json: #review.errors, status: :unprocessable_entity }
end
end
end
private
def post_params
params.require(:post).permit(:message)
end
end
In case you need it, here's the review model:
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :game
validates_presence_of :rating
validates_uniqueness_of :user_id
end
I don't understand why it's asking for a user_id or game_id, because it's only about the reviews..

You have to change the below
expect(assigns(:review)).to eq([review])
to
expect(assigns(:reviews)).to eq([review])
The reason is #reviews is the instance variable you have inside the #index controller action, not #review.

Related

Rails - How to pass controller tests when I use before_action

I need to run some tests and I have come at a stand still here.
I am using before_action in my Appointments controller
Here is the controller
class AppointmentsController < ApplicationController
before_action :set_appointment, only: %i[ show edit update destroy ]
#before we run anything if the user is not signed in show index and show functions
before_action :authenticate_user!, except: [:index,:show]
#only the correct user can edit,update and destroy
before_action :correct_user, only: [:edit, :update , :destroy]
# GET /appointments or /appointments.json
def index
#appointments = Appointment.all.decorate
end
# GET /appointments/1 or /appointments/1.json
def show
end
# GET /appointments/new
def new
##appointment = Appointment.new
#appointment = current_user.appointments.build
end
# GET /appointments/1/edit
def edit
end
#function to allow for search functionality
def search
#appointments = Appointment.where("date LIKE?", "%"+params[:q]+"%")
end
# POST /appointments or /appointments.json
def create
##appointment = Appointment.new(appointment_params)
#appointment = current_user.appointments.build(appointment_params)
#here underneath I am using my custom gem to filter bad words within the notes field when creating an appointment
#appointment.notes = Badwordgem::Base.sanitize(#appointment.notes)
respond_to do |format|
if #appointment.save
format.html { redirect_to appointment_url(#appointment), notice: "Appointment was successfully created." }
format.json { render :show, status: :created, location: #appointment }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #appointment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /appointments/1 or /appointments/1.json
def update
respond_to do |format|
if #appointment.update(appointment_params)
format.html { redirect_to appointment_url(#appointment), notice: "Appointment was successfully updated." }
format.json { render :show, status: :ok, location: #appointment }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #appointment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /appointments/1 or /appointments/1.json
def destroy
#appointment.destroy
respond_to do |format|
format.html { redirect_to appointments_url, notice: "Appointment was successfully destroyed." }
format.json { head :no_content }
end
end
#function here that restricts editing so the current logged in user can edit only their records
def correct_user
#appointment = current_user.appointments.find_by(id: params[:id])
redirect_to appointments_path, notice:"NOT ALLOWED TO EDIT THIS" if #appointment.nil?
end
private
# Use callbacks to share common setup or constraints between actions.
def set_appointment
#appointment = Appointment.find(params[:id])
end
# Only allow a list of trusted parameters through.
def appointment_params
params.require(:appointment).permit(:barber, :customer, :notes, :date,:user_id)
end
end
and here is my test controller
require "test_helper"
class AppointmentsControllerTest < ActionDispatch::IntegrationTest
Devise::Test::IntegrationHelpers
setup do
#appointment = appointments(:one)
end
test "should get index" do
get appointments_url
assert_response :success
end
test "should get new" do
get new_appointment_url
assert_response :success
end
test "should create appointment" do
assert_difference('Appointment.count') do
post appointments_url, params: { appointment: { barber: #appointment.barber, customer: #appointment.customer, date: #appointment.date, notes: #appointment.notes } }
end
assert_redirected_to appointment_url(Appointment.last)
end
test "should show appointment" do
get appointment_url(#appointment)
assert_response :success
end
test "should get edit" do
get edit_appointment_url(#appointment)
assert_response :success
end
test "should update appointment" do
patch appointment_url(#appointment), params: { appointment: { barber: #appointment.barber, customer: #appointment.customer, date: #appointment.date, notes: #appointment.notes } }
assert_redirected_to appointment_url(#appointment)
end
test "should destroy appointment" do
assert_difference('Appointment.count', -1) do
delete appointment_url(#appointment)
end
assert_redirected_to appointments_url
end
end
If I comment out the "before actions" in my controller , of course all the tests pass but with them 15 tests fail.
How do I make the tests pass with the before_action ?
For :authenticate_user just use the helper log_in in te test, after create a user, like this:
class AppointmentsControllerTest < ActionDispatch::IntegrationTest
let(:user) { User.new(user_params) }
......
and put
sign_ig user
inside each 'it methods you want'
For :set_appointment or :correct_user just passing right id params inside the path's call

NoMethodError:undefined method `employee' for nil:NilClass for RSPEC test Rails

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?

Tests failed after add 'belongs_to' to model in Rails application

Tests failed after add belongs_to in Rails
I have 2 models in Rails application:
class Micropost < ApplicationRecord
belongs_to :user # Test failed after add this string
validates :content, length: { maximum: 140 }, presence: true
end
class User < ApplicationRecord
has_many :microposts
validates :name, presence: true
validates :email, presence: true
end
I added string "belongs_to :user" to model "Micropost". After that I ran tests, and they failed:
rails test
1) Failure:
MicropostsControllerTest#test_should_create_micropost [/home/kiselev/project/toy_app/test/controllers/microposts_controller_test.rb:19]:
"Micropost.count" didn't change by 1.
Expected: 3
Actual: 2
2) Failure:
MicropostsControllerTest#test_should_update_micropost [/home/kiselev/project/toy_app/test/controllers/microposts_controller_test.rb:38]:
Expected response to be a <3XX: redirect>, but was a <200: OK>
I have these 2 tests:
test "should create micropost" do
assert_difference('Micropost.count') do
post microposts_url, params: { micropost: { content: #micropost.content, user_id: #micropost.user_id } }
end
assert_redirected_to micropost_url(Micropost.last)
end
test "should update micropost" do
patch micropost_url(#micropost), params: { micropost: { content: #micropost.content, user_id: #micropost.user_id } }
assert_redirected_to micropost_url(#micropost)
end
I have a controller "MicropostsController":
class MicropostsController < ApplicationController
before_action :set_micropost, only: [:show, :edit, :update, :destroy]
# POST /microposts
# POST /microposts.json
def create
#micropost = Micropost.new(micropost_params)
respond_to do |format|
if #micropost.save
format.html { redirect_to #micropost, notice: 'Micropost was successfully created.' }
format.json { render :show, status: :created, location: #micropost }
else
format.html { render :new }
format.json { render json: #micropost.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /microposts/1
# PATCH/PUT /microposts/1.json
def update
respond_to do |format|
if #micropost.update(micropost_params)
format.html { redirect_to #micropost, notice: 'Micropost was successfully updated.' }
format.json { render :show, status: :ok, location: #micropost }
else
format.html { render :edit }
format.json { render json: #micropost.errors, status: :unprocessable_entity }
end
end
end
Setup micropost:
class MicropostsControllerTest < ActionDispatch::IntegrationTest
setup do
#micropost = microposts(:one)
end
Params in Micropost controller:
def micropost_params
params.require(:micropost).permit(:content, :user_id)
end
Fixtures Micropost:
one:
content: MyText
user_id: 1
two:
content: MyText
user_id: 1
How can I improve these tests to pass?
belongs_to method adds among others also a presence validation for the user. Somewhere in the rails code it adds something like:
validates_presence_of :user
And it checks whether the user exists. In your fixtures you have set user_id: 1. But in your tests there is no user with 1 as an ID. To fix it you have to set correct user IDs for your microposts fixtures.
You can do it in the following way. You don't have to define user_id, you can define association in the fixtures:
one:
content: MyText
user: one
two:
content: MyText
user: one
Define a user key instead of user_id and as a value use the name of the fixture from the user fixtures - in tests it would be called users(:one) if you would want to access this fixture.
Note: You can also remove the presence validation by adding required: false to your belongs_to definition but I would not recommend it.

Controller test: "ShoppingList.count" didn't change by 1. Expected: 3 Actual: 2

I test the controlller "shopping_list". However when I start the test I get this error:
Failure:
ShoppingListsControllerTest#test_should_create_shopping_list [C:/Users/Clemens/meindorfladen/Server/test/controllers/shopping_lists_controller_test.rb:30]:
"ShoppingList.count" didn't change by 1.
Expected: 3
Actual: 2
So one parameter is missing, but how can this be? Does somebody know the answer? Here is the code:
shopping_lists.yml
shopping_list_drogerie:
user: user_heiko
name: Drogerie
created_at: <%= Time.now %>
updated_at: <%= Time.now %>
shopping_list_lebensmittel:
user: user_schmitt
name: Lebensmittel
created_at: <%= Time.now %>
updated_at: <%= Time.now %>
db/schema
create_table "shopping_lists", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.index ["user_id"], name: "index_shopping_lists_on_user_id", using: :btree
end
models/shopping_list.rb
class ShoppingList < ApplicationRecord
# db associations
belongs_to :user
# if a shopping list is deleted, also delete information about all items on the list
has_many :list_items, :dependent => :destroy
# if a shopping list is deleted, also delete information about who it was shared with
has_many :shared_lists , :dependent => :destroy
has_many :shared_with_users,through: :shared_lists, :source => :user
has_many :invitation
has_one :appointment
# validations
validates :user, :presence => true
validates :name, presence: true, allow_blank: false, uniqueness: {scope: :user_id}
end
controllers/shopping_lists_controller.rb
class ShoppingListsController < ApplicationController
load_and_authorize_resource
# GET /shopping_lists/1
# GET /shopping_lists/1.json
def show
end
# POST /shopping_lists
# POST /shopping_lists.json
def create
respond_to do |format|
if #shopping_list.save
format.html { redirect_to shopping_list_list_items_path(#shopping_list), alert: 'Shopping list was successfully created.' }
format.json { render :show, status: :created, location: #shopping_list }
else
format.html { render :new }
format.json { render json: #shopping_list.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /shopping_lists/1
# PATCH/PUT /shopping_lists/1.json
def update
respond_to do |format|
if #shopping_list.update(shopping_list_params)
format.html { redirect_to #shopping_list, notice: 'Shopping list was successfully updated.' }
format.json { render :show, status: :ok, location: #shopping_list }
else
format.html { render :edit }
format.json { render json: #shopping_list.errors, status: :unprocessable_entity }
end
end
end
# DELETE /shopping_lists/1
# DELETE /shopping_lists/1.json
def destroy
#shopping_list.destroy
respond_to do |format|
format.html { redirect_to shopping_lists_url, notice: 'Shopping list was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_shopping_list
#shopping_list = ShoppingList.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
private def shopping_list_params
params.require(:shopping_list).permit(:name)
end
end
EDIT: sorry I forgot the test-controller: shopping_lists_controller_test
require 'test_helper'
class ShoppingListsControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
include Warden::Test::Helpers
setup do
#drogerieShoppingList = shopping_lists(:shopping_list_drogerie)
#heiko = users(:user_heiko)
#heikoAppointment = appointments(:appointment_heiko)
end
test "should get index" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
get shopping_lists_url
assert_response :success
end
test "should get new" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
get new_shopping_list_url
assert_response :success
end
test "should create shopping_list" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
assert_difference('ShoppingList.count') do
#post shopping_lists_url, params: { shopping_list: #drogerieShoppingList.attributes, user_id: #heiko.id, appointment: #heikoAppointment }
post shopping_lists_url, params: { shopping_list: #drogerieShoppingList.attributes }
end
assert_redirected_to shopping_list_url(ShoppingList.last)
end
test "should show shopping_list" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
get shopping_list_url(#drogerieShoppingList)
assert_response :success
end
test "should get edit" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
get edit_shopping_list_url(#drogerieShoppingList)
assert_response :success
end
test "should update shopping_list" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
patch shopping_list_url(#drogerieShoppingList), params: { shopping_list: {name: 'WochenendEinkauf' } }
assert_redirected_to shopping_list_url(#drogerieShoppingList)
end
test "should destroy shopping_list" do
login_as(#heiko)
#heiko.confirmed_at = Time.now
assert_difference('ShoppingList.count', -1) do
delete shopping_list_url(#drogerieShoppingList)
end
assert_redirected_to shopping_lists_url
end
end
The problem isn't about the number of parameters.
Add the controller test code, because there's a test case that fails.
But when I look closer to your controller, the create method didn't actually created a #schopping_list. It just tries to save a #shopping_list variable which can easily be a nil.
UPDATE:
There seems to be two issues:
#create action in controller
The #sopping_list isn't initialized at all, You have to build the #shopping_list model before saving it. Like this:
def create
#shopping_list = SoppingList.new(shopping_list_params)
respond_to do |format|
if #shopping_list.save
...
Don't know whether the name field is the only field required to create a SoppingList model. As I see you've permitted only that one:
params.require(:shopping_list).permit(:name)

Updating attributes through a different object

I am having a ridiculously hard time trying to figure out how to do this. I have been at it literally all day.
I have an Account class and a Transaction class.
Accounts are created with a balance and I want the Transaction amount, depending on its type, to either add or subtract from the balance.
I want to be able to update the Account balance every time a transaction is created. This is for a personal finance application. As of now when I create a new transaction, nothing happens to the account balance.
accounts_controller.rb
class AccountsController < ApplicationController
def index
#accounts = Account.all
end
def show
#account = Account.find(params[:id])
end
def new
#account = Account.new
end
def edit
#account = Account.find(params[:id])
end
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.json { render json: #account, status: :created, location: #account }
else
format.html { render action: "new" }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
def update
#account = Account.find(params[:id])
respond_to do |format|
if #account.update_attributes(params[:account])
format.html { redirect_to #account, notice: 'Account was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
# DELETE /accounts/1
# DELETE /accounts/1.json
def destroy
#account = Account.find(params[:id])
#account.destroy
respond_to do |format|
format.html { redirect_to accounts_url }
format.json { head :no_content }
end
end
def update_balance
#a = Account.find(params[:id])
#a.transactions.each do |t|
#update_balance = t.t_type + #a.balance
#a.update_attributes(:balance => #update_balance)
end
end
end
transactions_controller.rb
class TransactionsController < ApplicationController
def create
#account = Account.find(params[:account_id])
#transaction = #account.transactions.create(params[:transaction])
redirect_to account_path(#account)
end
end
transaction.rb
class Transaction < ActiveRecord::Base
belongs_to :account
attr_accessible :amount, :category, :t_type
end
account.rb
class Account < ActiveRecord::Base
attr_accessible :balance, :name
has_many :transactions
end
If anyone has any idea what I'm doing wrong or can point me in the direction of a good thorough explanation, that would be great. I am so lost at this point.
Try this.
class Account < ActiveRecord::Base
attr_accessible :balance, :name
has_many :transactions
def update_with_transaction(transaction)
return unless self.transactions.include? transaction
if transaction.t_type.eql? SOME_TYPE
self.balance += transaction.ammount
else
self.balance -= transaction.ammount
end
save
end
end
class TransactionsController < ApplicationController
def create
account = Account.find(params[:account_id])
#transaction = account.transactions.create(params[:transaction])
account.update_with_transaction(#transaction)
redirect_to account_path(account)
end
end
It doesn’t update because you haven’t told it to. Create an after_create callback to do so:
class Transaction < ActiveRecord::Base
# ...
after_create :update_account_balance
private
def update_account_balance
account.balance += amount
account.save
end
end
Adjust as needed. Note that this won’t handle updates to a transaction’s amount, which is left as an exercise for the reader.

Resources