Rspec方法被称为2X,但无法找到第二次

时间:2016-02-19 01:12:23

标签: ruby-on-rails ruby-on-rails-4 rspec controller rspec-rails

这是我的控制器规范

before do
    @order = Order.new
end
it "should call find & assign_attributes & test delivery_start methods" do
    Order.should_receive(:find).with("1").and_return(@order)
    Order.any_instance.should_receive(:assign_attributes).with({"id"=>"1", "cancel_reason" => "random"}).and_return(@order)
    Order.any_instance.should_receive(:delivery_start).and_return(Time.now)
    post :cancel, order: {id:1, cancel_reason:"random"}
end

失败的是:

Failure/Error: Unable to find matching line from backtrace
   (#<Order:0x007fdcb03836e8>).delivery_start(any args)
       expected: 1 time with any arguments
       received: 2 times with any arguments
 # this backtrace line is ignored

但我不确定为什么基于此控制器操作调用delivery_start两次:

def cancel
    @order = Order.find(cancel_params[:id])
    @order.assign_attributes(cancel_params)
    if (@order.delivery_start - Time.now) > 24.hours
        if refund
            @order.save
            flash[:success] = "Your order has been successfully cancelled & refunded"
            redirect_to root_path
        else
            flash[:danger] = "Sorry we could not process your cancellation, please try again"
            render nothing: true
        end
    else
        @order.save
        flash[:success] = "Your order has been successfully cancelled"
        redirect_to root_path
    end
end

3 个答案:

答案 0 :(得分:1)

我建议你测试行为而不是实现。虽然有些情况下你想要在数据库中存根,但在控制器规范中这样做并不是一个好主意,因为你正在测试控制器和模型层之间的集成。

此外,您的测试只是测试您的控制器如何完成其​​工作 - 而不是实际完成它。

describe SomeController, type: :controller do

  let(:order){ Order.create } # use let not ivars.

  describe '#cancel' do

    let(:valid_params) do
      { order: {id: '123', cancel_reason: "random"} }
    end

    context 'when refundable' do
      before { post :cancel, params }
      it 'cancels the order' do
        expect(order.reload.cancel_reason).to eq "random"
        # although you should have a model method so you can do this:
        # expect(order.cancelled?).to be_truthy
      end

      it 'redirects and notifies the user' do
         expect(response).to redirect_to root_path
         expect(flash[:success]).to eq 'Your order has been successfully cancelled & refunded'
      end
    end
  end
end

答案 1 :(得分:0)

我建议更多的期望,并根据您的使用返回真或假。请考虑以下更改

class SomeController < ApplicationController
  def cancel
    ...
    if refundable?
      ...
    end
  end

  private

  def refundable?
    (@order.delivery_start - Time.now) > 24.hours
  end
end

# spec/controllers/some_controller_spec.rb
describe SomeController, type: :controller do
  describe '#cancel' do
    context 'when refundable' do
      it 'cancels and refunds order' do
        order = double(:order)
        params = order: {id: '123', cancel_reason: "random"}
        expect(Order).to receive(:find).with('123').and_return(order)
        expect(order).to receive(:assign_attributes).with(params[:order]).and_return(order)
        expect(controller).to receive(:refundable?).and_return(true)
        expect(controller).to receive(:refund).and_return(true)
        expect(order).to receive(:save).and_return(true)
        post :cancel, params
        expect(response).to redirect_to '/your_root_path'
        expect(session[:flash]['flashes']).to eq({'success'=>'Your order has been successfully cancelled & refunded'})
        expect(assigns(:order)).to eq order
      end
    end
  end
end

答案 2 :(得分:0)

对不起,这是一个非常不令人满意的答案,但我重新启动了我的电脑并且规范通过了......

之前令我讨厌的一件事是,我忘记了保存代码,即测试运行的代码的旧版本被称为delivery_start两次。但在这种情况下,我肯定检查过我已经救了。我不知道为什么重启会修复它...