我一直在使用in this article中描述的技术作为请求规范中控制器中的模拟方法。 TLDR,该技术使用“ allow_any_instance_of”模拟控制器方法。我发现了一些其他Stackoverflow帖子,这些帖子似乎与本文一致。
但是,在阅读rspec 3.8 documentation时,它显示以下内容:
- rspec-mocks API是为单个对象实例设计的,但是此功能可在整个对象类上运行。结果,在语义上有些混乱的情况。例如,在Expect_any_instance_of(Widget).to receive(:name).twice中,尚不清楚某个特定实例是否希望两次接收名称,或者是否希望总共接收两次。 (是前者。)
- 使用此功能通常是一种设计气味。可能是您的测试尝试执行太多操作,或者被测对象太复杂。
- 这是rspec-mocks的最复杂的功能,并且历来收到最多的bug报告。 (没有核心团队积极使用它,这无济于事。)
在请求规范中模拟方法的正确和“现代”方法是什么?
我认为以下内容无关紧要,以防万一:
编辑:添加一些示例代码。
module A
def my_pain
puts "I don't want to run this during testing"
end
end
class TestController < ApplicationController
include A
def index
my_pain
end
end
那么在控制器/请求测试中使用RSpec模拟“ #my_pain”时测试“ #index”的现代方法是什么。
答案 0 :(得分:-1)
我认为,我们不需要测试Controller,只需进行功能测试。 因为最终用户期望的是要呈现的内容,而不仅仅是导航。
答案 1 :(得分:-1)
另一种方法-尝试尽可能少地模拟。
在测试变慢或需要外部资源进行配置之前,不要进行任何模拟。
然后仅模拟缓慢的依赖项/类或访问外部
资源(网络服务,文件系统等)
在测试设置变得非常复杂之前,不要进行任何模拟。
然后模拟包含非常复杂的逻辑的依赖项,以简化/减少要测试的逻辑分支的数量。
如果您决定模拟一些依赖项:
如果在测试中向类提供了依赖项实例
class Order
def initialize(printer)
@printer = printer
end
def print
@printer.print("my-printer", self)
end
end
# Test
RSpec.describe "Order.print" do
it "prints order to my printer" do
fake_printer = double("Printer", :print => nil)
order = Order.new(fake_printer)
expect(fake_printer).to receive(:print).with("my-printer", order)
order.print
end
end
如果依赖项实例在测试中的类内部实例化了
class Order
def initialize
@printer = Printer.new("my-printer")
end
def print
@printer.print(self)
end
end
# Test
RSpec.describe "Order.print" do
it "prints order to my printer" do
fake_printer = double("Printer", :print => nil)
allow(Printer).to receive(:new).with("my-printer").and_return(fake_printer)
order = Order.new
expect(fake_printer).to receive(:print).with(order)
order.print
end
end
请注意,在使用double("ClassName")
时,您需要配置所有在测试过程中将被调用的方法,否则将引发异常。