【问题标题】:RSpec - unable to get all associate attributes in controllerRSpec - 无法在控制器中获取所有关联属性
【发布时间】:2016-09-17 20:44:41
【问题描述】:

Hello Programmers & Developers!!!,我是 RoR 的初学者,我在 Rails 中创建了一个简单的项目来学习它的工作原理,所以在那个项目中,我在为 create 方法编写规范时遇到了问题的控制器。当我试图在规范文件中传递对象的关联属性时,在控制器中它并没有获得所有属性。

subjects_controller.rb 文件的create 方法中。
我在这个变量中创建了一个名为attr 的变量,我存储了从subjects_controller_spec.rb 文件发送的所有值。

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

现在,如果我在控制台中使用p attr 打印attr 的值,它的输出就是我想要的确切输出,即

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

但是,现在我正在做 @subject = Subject.new(attr) 并且打印 @subject 的值会给出以下输出

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

在运行测试后,我的测试失败了,然后我打印了错误 p @subject.errors 它给了我以下输出

#<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"]}>

所以,我的实际问题是为什么 subjects_controller.rb 中的 @subject 没有 classroom_ids@987654339 的值@?如果有任何解决方案或建议,请帮助我解决此问题。



下面我为您提供所有必要的详细信息,以了解实际问题.

Ruby 版本 2.2.4
Rails 版本 4.2.0
数据库 MySQL

模型文件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



控制器文件 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
      attributes=@subject.attributes.merge(: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



控制器文件 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



工厂文件subjects.rb

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



RSpec 测试报告

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.


更多详情可以参考this Github link

提前感谢您的帮助。

【问题讨论】:

  • @subject.classroom_ids的值是多少?
  • @ruby_newbie 当我在 subjects_controller_spec.rb @subject.classroom_ids 中打印时,它给了我[1, 2]

标签: ruby-on-rails ruby ruby-on-rails-4 web-applications rspec


【解决方案1】:

[ "1", "2" ] 不是整数数组,而是字符串! @subject 具有classic_ids 和school_ids,但参数总是将输入值视为字符串,因此您的主题模型中会出现验证错误。所以尝试下面将字符串转换为整数:

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

这个怎么样?

在控制器中不使用map方法恢复如下:

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

在我的电脑中,通过从您的 github 链接修改主题模型如下并通过测试。 你能试试这个吗?

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

【讨论】:

  • 正如你所说,我更新了它attr=(params.require(:subject).permit(:name)).merge(:classroom_ids=&gt;params[:subject][:classroom_ids].map(&amp;:to_i),:school_ids=&gt;params[:subject][:school_ids].map(&amp;:to_i)),当我打印时它给了我{"name"=&gt;"Computer", "classroom_ids"=&gt;[1, 2], "school_ids"=&gt;[1]} ,这是确切的结果,但它仍然无法正常工作。终端错误还是一样。
  • 抱歉造成误会。我猜classic_ids 和school_ids 是数组,所以会出现验证错误。因此,您应该检查其中每个成员的数量。
  • 你有什么想法吗,怎么做?
  • 得到了NoMethodError: undefined method match for 1:Fixnum,请参考这个Github link,这样你就可以得到更准确的细节。
  • 好的,我回家后检查你的github链接,因为我现在不能克隆你的项目。你能试试 !id.match(/¥d+/) 到 !id.is_a?(Integer) 吗?你还没有通过测试吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
相关资源
最近更新 更多