我已将部分Foos控制器提取到新的rails模型中以执行操作:
foos_controller.rb
class FoosController < ApplicationController
respond_to :js
def create
@foo = current_user.do_something(@bar)
actioned_bar = ActionedBar.new(@bar)
actioned_bar.create
respond_with @bar
end
actioned_bar.rb
class ActionedBar
def initialize(bar)
@bar = bar
end
def create
if @bar.check?
# do something
end
end
end
我首先使用它,但现在我正在尝试回填rspec控制器测试。
我将测试各种模型方法,并将进行功能测试,以确保从这个角度来看它没问题,但我想添加一个测试以确保新的actioned_bar模型使用@bar从foos控制器调用。
我知道在rspec中你可以测试receives
with
某些arguments
某些 it "calls ActionedBar.new(bar)" do
bar = create(:bar)
expect(ActionedBar).to receive(:new)
xhr :post, :create, bar_id: bar.id
end
,但我很难让它发挥作用。
NoMethodError:
undefined method `create' for nil:NilClass
虽然这不起作用,但控制台报告:
expect(ActionedBar).to receive(:new)
这很奇怪,因为它只在我使用 it "calls ActionedBar.new(bar)" do
bar = create(:bar)
actioned_bar = ActionedBar.new(bar)
expect(actioned_bar).to receive(:create).with(no_args)
xhr :post, :create, bar_id: bar.id
end
时执行此操作,其余的控制器测试工作正常。
如果我尝试:
(#<ActionedBar:0xc8f9f74>).create(no args)
expected: 1 time with no arguments
received: 0 times with no arguments
控制台说:
put
如果我在运行测试时在控制器中执行了nil
;由于某种原因,此测试会导致控制器中的actioned_bar输出为public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (is_array($controller)) {
$controller = reset($controller);
}
if ($controller instanceof IInterface)
{
return false;
}
}
,但对所有其他控制器测试都很好。
我有没有办法测试在此控制器规范中调用ActionedBar?
答案 0 :(得分:1)
您可以使用expect_any_instance_of(ActionedBar).to receive(:create)
,因为规范和控制器中的实例是不同的实例。
如果您想使用原始对象,可以使用expect(ActionedBar).to receive(:new).and_call_original
(不会#new
只返回nil,您将获得NoMethodError
)。
答案 1 :(得分:0)
核心问题是您的规范中的actioned_bar
不会是控制器中ActionedBar
的同一个实例。因此,规范将始终失败。
相反,当调用new时,你需要让ActionedBar返回一个double:
it "calls ActionedBar.new(bar)" do
bar = create(:bar)
actioned_bar = instance_double("ActionedBar")
allow(ActionedBar).to receive(:new).and_return(actioned_bar)
expect(actioned_bar).to receive(:create).with(no_args)
xhr :post, :create, bar_id: bar.id
end
然而,我通常认为这种测试有一种代码味道 - 可以嘲笑外部合作者并设定你传递正确信息的期望。但您可能想要考虑您是否正在测试控制器如何完成其工作的细节而不是实际行为。
我发现最好设置一个调用控制器操作的规范,并设置对例如如何更改数据库状态或如何影响响应的期望。
答案 2 :(得分:0)
您可以设置由ActionedBar.new调用返回的双重ActionedBar,因为此实例与控制器中使用的实例不同。
describe "#create" do
let(:actioned_bar) { double(ActionedBar) }
let(:bar) { double(Bar) }
it "calls ActionedBar.new(bar)" do
expect(ActionedBar).to receive(:new).with(bar).and_returns(actioned_bar)
expect(actioned_bar).to receive(:create)
xhr :post, :create, bar_id: bar.id
end
end