Ruby on rails 创建后RSpec控制器测试失败

Ruby on rails 创建后RSpec控制器测试失败,ruby-on-rails,testing,rspec,Ruby On Rails,Testing,Rspec,我的程序按预期运行,但我的RSpec控制器测试代码中存在问题。我需要帮助对规范进行故障排除,这些规范是在我运行scaffold生成器时创建的。有三个“使用有效参数进行后期创建”失败: POST create with valid params creates a new Appointment (FAILED - 1) assigns a newly created appointment as @appointment (FAILED - 2) redirects to the c

我的程序按预期运行,但我的RSpec控制器测试代码中存在问题。我需要帮助对规范进行故障排除,这些规范是在我运行scaffold生成器时创建的。有三个“使用有效参数进行后期创建”失败:

POST create
with valid params
  creates a new Appointment (FAILED - 1)
  assigns a newly created appointment as @appointment (FAILED - 2)
  redirects to the created appointment (FAILED - 3)

Failures:

1) AppointmentsController POST create with valid params creates a new Appointment
 Failure/Error: expect {
   expected #count to have changed by 1, but was changed by 0
 # ./spec/controllers/appointments_controller_spec.rb:90:in `block (4 levels) in <top (required)>'

2) AppointmentsController POST create with valid params assigns a newly created appointment as @appointment
 Failure/Error: expect(assigns(:appointment)).to be_persisted
   expected `#<Appointment id: nil, member_id: nil, trainer_id: nil, created_at: nil, updated_at: nil, date: "2020-01-02", starts_at: "2000-01-01 08:00:00", ends_at: "2000-01-01 09:00:00">.persisted?` to return true, got false
 # ./spec/controllers/appointments_controller_spec.rb:99:in `block (4 levels) in <top (required)>'

3) AppointmentsController POST create with valid params redirects to the created appointment
 Failure/Error: expect(response).to redirect_to(Appointment.last)
   Expected response to be a <redirect>, but was <200>
 # ./spec/controllers/appointments_controller_spec.rb:104:in `block (4 levels) in <top (required)>'
}

以下是约会控制器的代码:

class AppointmentsController < ApplicationController
before_action :set_appointment, only: [:show, :edit, :update, :destroy]

# GET /appointments
# GET /appointments.json
def index
@appointments = Appointment.all
end

# GET /appointments/1
# GET /appointments/1.json
def show
end

# GET /appointments/new
def new
@appointment = Appointment.new
end

# GET /appointments/1/edit
def edit
预约模式的代码:

class Appointment < ActiveRecord::Base

belongs_to :member
belongs_to :trainer

validates_date :date, :on => :create, :on_or_after => :today

validates_time :starts_at, :between => ['06:30', '21:00']
validates_time :starts_at, :after => :now, :if => :today_appointment #scopes validation to current day only  
validates_time :ends_at, :after => :starts_at

validate :duration_of_appointment

validates :member, :trainer, presence: true

validates :starts_at, :ends_at, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "date",
:scope => "starts_at",    
:scope => "trainer_id"    
}

validates :starts_at, :ends_at, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "member_id"
}    


private

def today_appointment
Date.current == self.date     
end  


def duration_of_appointment
length = (ends_at - starts_at) / 60
return if length.between?(30, 120) # stops validation if length falls between the two integers
errors.add(:base, 'Duration must be between 30 and 120 minutes')
end
end
班级预约:create、:on\u或\u after=>:today
验证\u时间:在,:介于=>['06:30','21:00'之间开始\u
验证时间:开始时间::之后=>:now,:if=>:today\u约会只将验证范围设置为当天
验证\u时间:在处结束\u,:之后=>:在处开始\u
验证:预约的持续时间
验证:成员,:培训师,状态:真
验证:起始点在,:结束点在,:重叠=>{
:exclude_edges=>[“开始于”,“结束于”],
:scope=>“日期”,
:scope=>“开始于”,
:范围=>“培训师id”
}
验证:起始点在,:结束点在,:重叠=>{
:exclude_edges=>[“开始于”,“结束于”],
:scope=>“成员id”
}    
私有的
def今日预约
Date.current==self.Date
结束
def预约的持续时间
长度=(结束时-开始时)/60
返回if length.between?(30,120)#如果长度介于两个整数之间,则停止验证
错误。添加(:base,“持续时间必须介于30到120分钟之间”)
结束
结束

您不应该向控制器提供成员和培训师的内置实例。相反,创建成员和培训师,并将其id作为
成员id
传递,
trainer\u id
位于有效的\u属性哈希中。 首先,创建所需的培训师和成员:

before :each do
  @trainer = FactoryGirl.build(:trainer)
  @member = FactoryGirl.build(:member)
end
然后在哈希中使用它们的ID:

let(:valid_attributes) { {
  'date' => '2020-01-02',
  'starts_at' => '08:00:00',
  'ends_at' => '09:00:00',    
  'member_id' => @member.id,
  'trainer_id' => @trainer.id 
}

在您的代码中,您甚至没有创建它们,您只创建了它们,但仅将
build(:trainer)
更改为
create(:trainer)
对您的情况无效。

谢谢您的回复,Felix。我正在学习Rails和RSpec,不完全理解您的建议。我有会员和培训师工厂,我是否使用它们创建两个类的实例(即会员和培训师)?我应该如何修改现有的“valid\u attributes”散列来传递ID?谢谢你的帮助!Felix-我的控制器测试在我将“FactoryGirl.build”更改为“FactoryGirl.create”后全部通过:每个块保存成员和培训师实例。正在将ID传递给控制器;所有规格均为绿色。稍后我将重构我的规格,使其干燥,并使用我的工厂。非常感谢你的帮助!!
class Appointment < ActiveRecord::Base

belongs_to :member
belongs_to :trainer

validates_date :date, :on => :create, :on_or_after => :today

validates_time :starts_at, :between => ['06:30', '21:00']
validates_time :starts_at, :after => :now, :if => :today_appointment #scopes validation to current day only  
validates_time :ends_at, :after => :starts_at

validate :duration_of_appointment

validates :member, :trainer, presence: true

validates :starts_at, :ends_at, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "date",
:scope => "starts_at",    
:scope => "trainer_id"    
}

validates :starts_at, :ends_at, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "member_id"
}    


private

def today_appointment
Date.current == self.date     
end  


def duration_of_appointment
length = (ends_at - starts_at) / 60
return if length.between?(30, 120) # stops validation if length falls between the two integers
errors.add(:base, 'Duration must be between 30 and 120 minutes')
end
end
before :each do
  @trainer = FactoryGirl.build(:trainer)
  @member = FactoryGirl.build(:member)
end
let(:valid_attributes) { {
  'date' => '2020-01-02',
  'starts_at' => '08:00:00',
  'ends_at' => '09:00:00',    
  'member_id' => @member.id,
  'trainer_id' => @trainer.id 
}