我的控制器中有一个操作,它使用我无法控制的API中的对象实例加载实例变量:
def index
@obj = MyObj.find(id: params[:id])
end
MyObj.find进行API调用并返回我返回的内容。
现在说我想为我的视图编写一个测试,但我不能使用测试数据库,因为我的应用程序依赖于该API。我永远不会指望API返回一个可测试对象,我想尝试的一些测试依赖于该对象的状态。
我希望能够在渲染我的视图之前手动创建自己的测试@obj
并让我的测试工作在由@obj
通知的视图渲染上。
理想情况下会是这样的:
before(:all) do
@obj = {attr1: "abc", attr2: 123}
driver.navigate.to("#{ENV['RAILS_HOST']}/my_view/123")
end
这显然不起作用。有没有办法做到这一点?
编辑:尝试存根该方法似乎不起作用,这是它目前的样子:
规格:
before(:each) do
allow(Library::Equipment)
.to receive(:mymethod)
.with(123)
.and_return("Stub method")
puts Library::Equipment.mymethod(123)
#prints "Stub method"
driver.navigate.to("#{ENV['RAILS_HOST']}/library/equipments/123/variables")
end
到/library/equipments/:equipment_id/variables
的 library/variables#index
路由,如下所示:
def index
@test = Library::Equipment.mymethod(123)
puts @test
#prints "Real method"
# other code...
end
我的Library::Equipment
类有这个类方法:
def self.mymethod(param)
"Real method"
end
在我的index.html.erb
内,我只需<%= @test %>
即可查看其中包含的内容。正如您所看到的,从我的spec文件和mymethod
操作
index
的返回值会有所不同
答案 0 :(得分:1)
您可以通过rspec's allow方法将呼叫存根到MyObj.find
:
let(:obj) { { attr1: "abc", attr2: 123 } }
let(:obj_id) { 123 }
before(:each) do
allow(MyObj)
.to receive(:find)
.with(id: obj_id)
.and_return(obj)
driver.navigate.to("#{ENV['RAILS_HOST']}/my_view/123")
end
allow
将为您提供模拟方法调用响应的可能性(例如find
)并返回您选择的答案。这样,当您的控制器调用MyObj.find
时,不会调用find
的实际实现,而是调用返回通过and_return
指定的对象的rspec代码。 with
方法仅用于缩小模拟范围,以便模拟在参数不匹配时不响应。在您的情况下,它可能是可选的。
请注意,我已将before(:all)
更改为before(:each)
。前者在整个套件之前只执行一次。由于您需要进行独立测试,因此您不希望有一个更改obj
,这将会溢出到下一个测试。此外,似乎无法访问let
块中的before(:all)
鉴于Selenium测试通常是验收测试,我会发现这样的嘲弄是可疑的。
如果您无法访问外部API,那么提供canned responses可能会让您感到恼火,但至少存在明确的图层。