Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RSpec - unable to get all associate attributes in controller

Hello Programmers & Developers!!!, I'm a beginner in RoR and creating a simple project in rails to learn its working, so in that project I'm facing a problem in writing a spec for the create method of controller. When I'm trying to pass the associate attributes of the object in spec file, in controller it isn't get all the attributes.

In the create method of subjects_controller.rb file.
I've created a variable called attr in this variable I'm storing all the values sent from subjects_controller_spec.rb file.

attr=(params.require(:subject).permit(:name)).merge(:classroom_ids=>params[:subject][:classroom_ids],:school_ids=>params[:subject][:school_ids])

Now, If I print the value of the attr using p attr in console it's output is the exact output that I want, which is

{"name"=>"Computer", "classroom_ids"=>["1", "2"], "school_ids"=>["1"]}

But, now I'm doing @subject = Subject.new(attr) and printing value of @subject gives the following output

#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>

and after running the test I'm getting my test failed and then I printed the error p @subject.errors it gave me the below output

#<ActiveModel::Errors:0x007fc35444a218 @base=#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>, @messages={:school_ids=>["is not a number"], :classroom_ids=>["is not a number"]}>

So, here is my actual question is why @subject in subjects_controller.rb is not having values of classroom_ids and school_ids? If any solution or suggestion is there then please help me to sort out this problem.



Below I'm providing you all the necessary details to understand the actual problem.

Ruby Version 2.2.4
Rails Version 4.2.0
Database MySQL

Model file subject.rb

class Subject < ActiveRecord::Base
  has_and_belongs_to_many :schools
  has_and_belongs_to_many :teachers
  has_and_belongs_to_many :classrooms
  has_and_belongs_to_many :students

  validates_presence_of :name, :school_ids, :classroom_ids
  validates_numericality_of :school_ids, :classroom_ids
end



Controller file subjects_controller_spec.rb

require 'rails_helper'

RSpec.describe SubjectsController, type: :controller do
  before(:each) do
    @school1 = FactoryGirl.create(:school)
    @classroom1 = FactoryGirl.create(:classroom, :school_id=>@school1.id)
    @classroom2 = FactoryGirl.create(:classroom, :school_id=>@school1.id)
    @subject = FactoryGirl.build(:subject)
    @subject.classrooms<<@classroom1
    @subject.classrooms<<@classroom2
    @subject.schools<<@school1
  end
  context "POST create" do
    it "should be success" do
      # p @subject
      # p @subject.classrooms
      # p @subject.classroom_ids
      [email protected](:classroom_ids=>@subject.classroom_ids,:school_ids=>@subject.school_ids)

      # In below line, I'm sending all the values to the controller to create a new subject.

      post :create, :subject=>attributes

      response.status.should eq 201
    end
  end
end



Controller file subjects_controller.rb

class SubjectsController < ApplicationController
  before_action :set_subject, only: [:show, :edit, :update, :destroy]

  # GET /subjects
  def index
    @subjects = Subject.all
  end

  # GET /subjects/1
  def show
  end

  # GET /subjects/new
  def new
    @subject = Subject.new
  end

  # GET /subjects/1/edit
  def edit
  end



  # POST /subjects
  def create
    attr=(params.require(:subject).permit(:name)).merge(:classroom_ids=>params[:subject][:classroom_ids],:school_ids=>params[:subject][:school_ids])

    p attr ### here it prints all the values which I want to create subject.###

    @subject = Subject.new(attr)

    p @subject ### here is the actual problem, It's not printing all the values that need to create a new subject.###

    if @subject.save
      redirect_to @subject, notice: 'Subject was successfully created.', status: :created
    else
      p @subject.errors
      render :new, status: :unprocessable_entity
    end
  end



  # PATCH/PUT /subjects/1
  def update
    if @subject.update(subject_params)
      redirect_to @subject, notice: 'Subject was successfully updated.', status: :ok
    else
      render :edit, :status => :unprocessable_entity
    end
  end

  # DELETE /subjects/1
  def destroy
    @subject.destroy
    redirect_to subjects_url, notice: 'Subject was successfully destroyed.'
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_subject
      @subject = Subject.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def subject_params
      params.require(:subject).permit(:name, :school_ids, :classroom_ids)
    end
end



Factory file subjects.rb

FactoryGirl.define do
  factory :subject do
    name "Computer"
  end
end



RSpec Test Report

rspec spec/controllers/subjects_controller_spec.rb 
{"name"=>"Computer", "classroom_ids"=>["1", "2"], "school_ids"=>["1"]}
#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>
#<ActiveModel::Errors:0x007fcdfe8f1a28 @base=#<Subject id: nil, name: "Computer", created_at: nil, updated_at: nil>, @messages={:school_ids=>["is not a number"], :classroom_ids=>["is not a number"]}>
F

Failures:

  1) SubjectsController POST create should be success
     Failure/Error: response.status.should eq 201

       expected: 201
            got: 422

       (compared using ==)
     # ./spec/controllers/subjects_controller_spec.rb:21:in `block (3 levels) in <top (required)>'

Deprecation Warnings:

Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }` instead. Called from /Users/vishal/project/school_system/spec/controllers/subjects_controller_spec.rb:21:in `block (3 levels) in <top (required)>'.


If you need more of the backtrace for any of these deprecations to
identify where to make the necessary changes, you can configure
`config.raise_errors_for_deprecations!`, and it will turn the
deprecation warnings into errors, giving you the full backtrace.

1 deprecation warning total

Finished in 0.40113 seconds (files took 3.03 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/controllers/subjects_controller_spec.rb:14 # SubjectsController POST create should be success

Coverage report generated for RSpec to /Users/vishal/project/school_system/coverage. 49 / 332 LOC (14.76%) covered.


For more details you can refer this Github link.

Thanks For Help In Advance.

like image 512
Vishal Nagda Avatar asked Nov 08 '22 08:11

Vishal Nagda


1 Answers

[ "1", "2" ] is not array of integer but String! @subject has classroom_ids and school_ids, but params always treat input values as String, so validation error occurs in your Subject model. So try below to transform String to Integer:

params[:subject][:classroom_ids].map(&:to_i) 
params[:subject][:school_ids].map(&:to_i) 

How about this?

restore below without map method in the controller:

params[:subject][:classroom_ids] 
params[:subject][:school_ids]

In my PC, by modifing the Subject model as below from your github link and passed the test. Could you try this?

Class Subject < ActiveRecord::Base
  has_and_belongs_to_many :schools
  has_and_belongs_to_many :teachers
  has_and_belongs_to_many :classrooms
  has_and_belongs_to_many :students

  validates_presence_of :name, :school_ids, :classroom_ids
  validate :validate_classroom_ids
  validate :validate_school_ids

  private

  def validate_classroom_ids
    if classroom_ids.any?{ |id| !id.is_a?(Integer) }
      errors.add(:classroom_ids, 'is not a number')
      return false
    end
  end

  def validate_school_ids
    if school_ids.any?{ |id| !id.is_a?(Integer) }
      errors.add(:school_ids, 'is not a number')
      return false
    end
  end
end
like image 66
YTorii Avatar answered Nov 15 '22 05:11

YTorii