Tests failed after add 'belongs_to' to model in Rails application - ruby-on-rails

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.

Related

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?

CRUD method for a model that belongs_to many models Rails

I'm new to Rails and I might be missing something very basic here:
User can create contact for both branches and division of a company
Branch.rb
class Branch < ApplicationRecord
belongs_to :company
has_many :contacts
end
Division.rb
class Division < ApplicationRecord
belongs_to :company
has_many :contacts
end
Contact.rb
class Contact < ApplicationRecord
belongs_to :branch
belongs_to :division
end
Now a user can create a contact from the branch page where there is no division_id and can create a contact from the Division page.
I have defined my routes.rb like this:
Routes.rb
resources :companies, :shallow => true do
get 'company_page'
resources :branches, :shallow => true do
get 'branch_page'
resources :contacts
end
resources :divisions, :shallow => true do
get 'division_page'
resources :contacts
end
end
As a result, if I create a contact from either Branch or Division, it goes to contacts#create method.
In my contacts_controller.rb, I have:
def create
#newContact = Contact.new(contact_params)
id = #division = #branch = nil
isBranch = false
if params[:branch_id] != nil
isBranch = true
id = params[:branch_id]
else
isBranch = false
id = params[:division_id]
end
if isBranch
branch = Branch.find(id)
#newContact.branch = branch
#branch = branch
else
division = Division.find(id)
#newContact.division = division
#division = division
end
respond_to do |format|
if #newContact.save
format.js
format.html { render :nothing => true, :notice => 'Contact created successfully!' }
format.json { render json: #newContact, status: :created, location: #newContact }
else
format.html { render action: "new" }
format.json { render json: #newContact, status: :unprocessable_entity }
end
end
end
But I have facing ActiveRecord Error during #newContact.save.
I'm sure I am doing something fundamentally very wrong here and Rails handles such things in another elegant way which I don't know of.
As #Anthony noted, you'll need to make your belongs_to associations optional:
# app/models/contact.rb
class Contact < ApplicationRecord
belongs_to :branch, optional: true
belongs_to :division, optional: true
end
But another problem is that params[:division_id] and params[:branch_id] are always nil. Both of those keys exist inside the [:contact] key. So the error you are getting should be ActiveRecord::RecordNotFound: Couldn't find Division with 'id'=
All that conditional logic is unnecessary. You can just make a new contact with whatever params are given. Also, you should be using Ruby convention for variable naming, which is snake_case instead of camelCase.
Finally, I assume you'll want to redirect HTML requests to either the branch or the division show page, depending on which was associated. So I've added logic to do that.
Here's a quick refactoring of the controller #create action:
def create
#new_contact = Contact.new(contact_params)
if #new_contact.save
branch = #new_contact.branch
division = #new_contact.division
redirect_path = branch ? branch_path(branch) : division_path(division)
respond_to do |format|
format.js
format.html { redirect_to redirect_path, :notice => 'Contact created successfully!' }
format.json { render json: #new_contact, status: :created, location: #new_contact }
end
else
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #new_contact, status: :unprocessable_entity }
end
end
end
This proves that it works:
# spec/controllers/contacts_controller_spec.rb
require 'rails_helper'
RSpec.describe ContactsController, type: :controller do
let(:company) { Company.create!(name: 'Company Name') }
let(:division) { Division.create!(name: 'Division Name', company: company) }
let(:branch) { Branch.create!(name: 'Branch Name', company: company) }
describe '#create' do
context 'when created with a division id' do
let(:attributes) { {'division_id' => division.id, 'name' => 'Contact Name'} }
it 'creates a contact record and associates it with the division' do
expect(Contact.count).to eq(0)
post :create, params: {contact: attributes}
expect(Contact.count).to eq(1)
contact = Contact.first
expect(contact.division).to eq(division)
end
end
context 'when created with a branch id' do
let(:attributes) { {'branch_id' => branch.id, 'name' => 'Contact Name'} }
it 'creates a contact record and associates it with the branch' do
expect(Contact.count).to eq(0)
post :create, params: {contact: attributes}
expect(Contact.count).to eq(1)
contact = Contact.first
expect(contact.branch).to eq(branch)
end
end
end
end

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)

Rspec test got: nil

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.

How to perform validation on associated attributes

I tried to validate associated attribute access_module_id in role
model but it doesn't work. How to give validation of presence: true to other model's attribute in role form? Here is associations
role.rb
class Role < ActiveRecord::Base
....
has_many :access_module_roles, :dependent => :destroy
has_many :access_modules, through: :access_module_roles
validates :name,:access_module_id, presence: true # I want to validate presence of access_module_ids in role form
end
access_module.rb
class AccessModule < ActiveRecord::Base
has_many :access_module_roles
has_many :roles, through: :access_module_roles
end
access_module_roles.rb
class AccessModuleRole < ActiveRecord::Base
belongs_to :access_module
belongs_to :role
end
Update
I have tried below validation and if I select one, two or all still getting an error like
"Access module ids can't be blank"
validates_presence_of :access_module_ids
Controller
def create
#role = Role.new(role_params)
respond_to do |format|
if #role.save
params[:role][:access_module_ids].each do |acmi|
AccessModuleRole.create!(:role_id => #role.id, :access_module_id => acmi) if acmi!=""
end
format.html { redirect_to roles_path, notice: 'Role was successfully created.' }
format.json { render :index, status: :created, location: #role }
else
format.html { render :new }
format.json { render json: #role.errors, status: :unprocessable_entity }
end
end
end
I found that I am doing mistake in Create method. I was inserting data to AccessModuleRole after doing save so it gets validation error while creating..And was getting parameter as nil
Corrected code:
def create
#role = Role.new(role_params)
#role.access_module_ids = params[:role][:access_module_ids]
respond_to do |format|
if #role.save
format.html { redirect_to roles_path, notice: 'Role was successfully created.' }
format.json { render :index, status: :created, location: #role }
else
format.html { render :new }
format.json { render json: #role.errors, status: :unprocessable_entity }
end
end
end
Permitted Role's attributes:
private
def role_params
params.require(:role).permit(:name,:chk_ids ,:description, :code, :is_active, :access_module_ids)
end
Now it works perfectly.. Thanks to RAJ to pointing me :)

Resources