期望接收any_instance的工作,但不是特定的工作

时间:2014-09-11 22:44:48

标签: ruby-on-rails ruby rspec

我有以下rspec:

context 'when there is an incoming error' do
      it 'should update status of url to error' do 
        url = create(:url)
        error_params = {error: 'whatever', url_id : url.id}

        expect(url).to receive(:set_error)
        post :results, error_params
      end
end

结果动作如下:

def results
  url = Url.find(url_id: params['url_id'])
  if params.key?('error') && !params['error'].blank?
      url.set_error
  end
end

如果我这样做,测试不会通过:

expected: 1 time with any arguments
received: 0 times with any arguments

但是,如果我改为:

expect_any_instance_of(Url).to receive(:set_error)

它过去了。我只有一个网址,所以我不确定发生了什么。

1 个答案:

答案 0 :(得分:1)

当您创建to receive期望时,它会连接到特定的Ruby对象。

调用results操作时,它会实例化 new url对象。它表示您在Rspec示例中调用期望的相同数据库对象。但它不是同一个Ruby对象 - 它是一个带有(可能)相同数据的新对象。所以期望失败了。

举例说明:

describe ".to_receive" do
  it "works on Ruby objects" do
    url = Url.create(:url)
    same_url = Url.find(url.id)
    expect(url).to_not receive(:to_s)
    same_url.to_s
  end
end

要(稍微)获得您可以使用any_instance的所需行为并更改控制器,以便将url对象分配给实例变量。通过这种方式,您可以更轻松地检查url对象:

# Change your action so that it saves the url object as an instance variable
def results
  @url = Url.find(url_id: params['url_id'])
  if params[:error].present?
    @url.set_error
  end
end

# Change your spec to look at the assigned variable
context 'when there is an incoming error' do
  it 'should update status of url to error' do 
    url = create(:url)
    error_params = {error: 'whatever', url_id: url.id}
    expect_any_instance_of(Url).to receive(:set_error)
    post :results, error_params
    expect(assigns(:url)).to eq(url)
  end
end

由于assigns只允许您在执行控制器操作后检查指定的ivar ,因此您无法使用它来创建接收期望。