我认为,这不是存根的真正问题,而是一般方法。在为某个类编写单元测试时,您应该坚持该类的功能,并最终坚持它所看到的 API。如果您正在使用“内部”out 或 Interface - 对于 Session 的规格来说已经太多了。
Session 真正看到的是Interfaces 公共hello 方法,因此Session 规范不应该知道它的内部实现(它是@out.puts "hello")。您真正应该关注的唯一一件事是,hello 方法已被调用。另一方面,确保为hello 调用put 应在Interface 的规范中进行描述。
Ufff...这是很长的介绍/解释,但是如何进行呢? (也称为给我看代码!;))。
话虽如此,Session.new 应该只知道Interfaces hello 方法,它应该相信它可以正常工作,并且Sessions 规范应该确保调用该方法。为此,我们将使用spy。让我们把手弄脏!
RSpec.describe Session do
let(:fake_interface) { spy("interface") }
let(:session) { Session.new }
before do
allow(Interface).to receive(:new).and_return(fake_interface)
end
describe "#new" do
it "creates an instance of Session" do
expect(session).to be_an_instance_of(Session) # this works now!
end
it "calls Interface's hello method when initialized" do
Session.new
expect(fake_interface).to have_received(:hello)
end
end
end
test spy 是一个函数,它记录参数、返回值、this 的值以及所有调用所引发的异常(如果有)。
这来自SinonJS(这是谷歌搜索“什么是测试间谍”时的第一个结果),但解释是准确的。
这是如何工作的?
Session.new
expect(fake_interface).to have_received(:hello)
首先,我们正在执行一些代码,然后我们断言预期的事情已经发生。从概念上讲,我们希望确定,在Session.new 期间,fake_interface have_received(:hello)。就是这样!
好的,但我需要另一个测试来确保 Interfaces 方法是使用 特定 参数调用的。
好的,让我们测试一下!
假设Session 看起来像:
class Session
def initialize
@interface = Interface.new(self)
@interface.hello
@interface.say "Something More!"
end
end
我们要测试say:
RSpec.describe Session do
describe "#new" do
# rest of the code
it "calls interface's say_something_more with specific string" do
Session.new
expect(fake_interface).to have_received(:say).with("Something More!")
end
end
end
这个很简单。
还有一件事——我的Interface 将Session 作为参数。如何测试interface调用sessions方法?
让我们看一下示例实现:
class Interface
# rest of the code
def do_something_to_session
@session.a_session_method
end
end
class Session
# ...
def another_method
@interface.do_something_to_session
end
def a_session_method
# some fancy code here
end
end
如果我说的话,这并不奇怪......
RSpec.describe Session do
# rest of the code
describe "#do_something_to_session" do
it "calls the a_session_method" do
Session.new.another_method
expect(fake_interface).to have_received(:do_something_to_session)
end
end
end
您应该检查,如果Sessions another_method 调用了interfaces do_something_to_session 方法。
如果您像这样进行测试,您可以使测试对未来的变化不那么脆弱。您可以更改Interface 的实现,使其不再依赖put。引入此类更改时 - 您只需更新 Interface 的测试即可。 Session 只知道调用了正确的方法,但里面发生了什么?这就是Interfaces 的工作...
希望对您有所帮助!请在我的other answer 中查看spy 的另一个示例。
祝你好运!