0

我正在尝试使用模型的 rspec 模拟在控制器上创建测试,它似乎只有在我说

Type.any_instance.should_recieve(...)

代替

instancename.should_receive(...)

我的代码看起来像这样。(通常我使用FactoryGirl,但我不在这个例子中以确保它不是问题)

it "calls blah on foo" do
  foo = Foo.new
  foo.save
  foo.should_receive(:blah) #this fails because it's called 0 times
  #Foo.any_instance.should_receive(:blah) #this would succeed
  post :create, {:foo => foo}
end

在我的控制器中

def create
  foo = Foo.find_by_id(params[:foo])
  foo.blah
  #other stuff thats not related
end

我知道我可以模拟 Foo.find_by_id 并让它返回 foo,但我觉得我不需要这样做,因为它应该被返回,这意味着如果我停止使用 find_by_id,测试就会中断,这真的不是一个重要的细节。

知道我做错了什么吗?如果我不必到处都说 any_instance 并且不必模拟 find_by_id,我觉得我的测试会更好。

4

1 回答 1

2

您的代码不起作用,因为它与实际代码中调用的foo 对象不同。blah

在您的规范代码中,您创建一个实例并保存它:

foo = Foo.new
foo.save

这会将记录保存到指向的数据库foo中。然后,您对对象提出期望:

foo.should_receive(:blah)

只有当规范和应用代码指向同一个对象时,这种期望才会起作用。正如您所注意到的,您可以通过例如存根find_by_id返回它来实现这一点。或者,您也可以对任何实例设置期望值,您也会注意到这一点。

但是,期望不会按原样工作。在您的实际代码中,您创建了一个不同的对象foo

foo = Foo.find_by_id(params[:foo])
foo.blah

如果params[:foo]是记录的 id,那么foo在您的规范代码和foo应用程​​序代码中都指向同一个记录,但这并不意味着它们是同一个对象(它们不是)。

另外,如果我理解正确,我相信这一点:

post :create, {:foo => foo}

应该:

post :create, {:foo => foo.id}

因此,简而言之,如果您想要的是消息期望,则需要存根find或将期望应用于任何实例。(请注意,您应该能够存根find而不是find_by_id,因为find无论如何动态查找器都会调用,这应该使您的测试更加健壮。)

希望有帮助。

于 2013-04-29T04:27:20.313 回答