Rspec 3.3 - 测试在控制器

时间:2015-10-08 01:05:06

标签: ruby-on-rails-3.2 rspec3

我有一个看起来像这样的约会控制器:

class AppointmentsController < ApplicationController
  def update
    @appointment = Appointment.find(params[:id])
    if @appointment.update_attributes(params[:appointment])
      @appointment.update_order_time_remaining
      redirect_to order_path(@appointment.order)
    else
     redirect_to :back
    end
  end
end

我正在尝试编写一个测试,以验证在@appointment上调用update_order_time_remaining。测试看起来像:

RSpec.describe AppointmentsController, :type => :controller do
  describe "PUT update" do
    it "calls update_order_time_remaining" do
      @appt = Appointment.create(FactoryGirl.attributes_for(:appointment))
      expect(controller.instance_variable_get :@appointment).to receive(:update_order_time_remaining)
      put :update, appointment: {status: 'confirmed'}, id: @appt.id
    end
  end
end

代码已经简化了一点。我有其他约会控制器测试正在通过,但我不能让这个测试通过。我一直收到失败的消息:

Failure/Error: expect(controller.instance_variable_get :@appointment).to receive(:update_order_time_remaining) (#<Appointment:0x00000009044b48>).update_order_time_remaining(*(any args)) expected: 1 time with any arguments received: 0 times with any arguments

我还尝试使用assigns(:appointment)代替controller.instance_variable_get :@appointment

更新

我使用此代码进行测试:

it "calls update_order_time_remaining" do
  @appt = Appointment.create(FactoryGirl.attributes_for(:appointment))
  expect_any_instance_of(Appointment).to receive(:update_order_time_remaining)
  put :update, appointment: {durations: ['60'], status: 'confirmed'}, id: @appt.rand_id
end

这似乎有些不准确,但你觉得这已经足够了吗?

1 个答案:

答案 0 :(得分:0)

也可能没事。

另一个选择是让约会返回完全相同的实例,例如:

expect(Appointment).to receive(:find).with(appt.rand_id).and_return(appt)
expect(appt).to receive(:update_order_time_remaining)

(在这种情况下不需要在测试中使用实例变量)

在这种情况下,您可以使用mock(而不是实际创建的记录)来更快地进行测试。

为什么要用如此奇怪的代码创建约会? Appointment.create(FactoryGirl.attributes_for(:appointment))

如果内置记录无效,则不会保存到DB,但您不会知道。这可能导致奇怪的测试失败。

1)FactoryGirl具有创建方法,该方法较短,如果出现错误则会失败

2)如果您的记录无效,最好使用爆炸版本create!失败

UPD 示例测试:

it "calls update_order_time_remaining" do
  appointment = FactoryGirl.build_stubbed(:appointment)
  expect(Appointment).to receive(:find).with(appointment.rand_id).and_return(appointment)
  expect(appointment).to receive(:update_order_time_remaining)
  put :update, appointment: {durations: ['60'], status: 'confirmed'}, id: appointment.rand_id
end