双重测试是单元测试中的一种模式。它用特定于测试场景的等效实现替换测试相关组件。
这些实现被称为 doubles,因为尽管它们的行为可能特定于测试,但它们的行为类似于它们模拟的对象,并且具有相同的 API。
间谍是 Jasmine 测试双打的解决方案。
在其核心,Jasmine 间谍是一种特殊类型的功能,记录所有与它发生的互动。因此,当返回值或对象状态的变化不能用于确定测试预期是否成功时,它们非常有用。
或者换句话说当测试成功只能通过行为检查来确定时,Jasmine 间谍是完美的。
为了理解行为检查的概念,让我们重温第 3 章、测试前端代码中给出的例子,并测试NewInvestmentView
的可观察行为:
describe("and when an investment is created", function() {
var callbackSpy;
var investment;
beforeEach(function() {
callbackSpy = jasmine.createSpy('callback');
view.onCreate(callbackSpy);
investment = view.create();
});
it("should invoke the 'onCreate' callback with the investment",function() {
expect(callbackSpy).toHaveBeenCalled();
expect(callbackSpy).toHaveBeenCalledWith(investment);
});
});
在规范设置期间,它使用jasmine.createSpy
函数创建一个新的 Jasmine 间谍,同时为其传递一个名称(callback
),然后它使用onCreate
函数将该间谍设置为视图创建事件的观察者,最后它调用create
函数创建一个新的投资。
稍后在期望中,规范使用匹配器的toHaveBeenCalled
和toHaveBeenCalledWith
来检查是否调用了callbackSpy
以及参数是否正确(investment
,从而进行行为检查。
本身的间谍非常有用,但是的真正威力来自于使用对应的间谍来改变对象的原始实现。
考虑下面的例子,它想要验证当提交表单时,必须调用view
的create
函数。
describe("and when the form is submitted", function() {
beforeEach(function() {
spyOn(view, 'create');
view.$el.submit();
});
it("should create an investment", function() {
expect(view.create).toHaveBeenCalled();
});
});
这里我们利用全局 Jasmine 功能 spyOn
通过一个间谍来改变view
的create
功能。
随后在规范中,我们使用toHaveBeenCalled
Jasmine 匹配器来验证view.create
函数被调用。
规范完成后,Jasmine 恢复对象的原始行为。
DOM 事件在编写前端应用时一直被使用,有时我们想编写一个规范来检查事件是否被触发。
一个事件可能是类似于表单提交或输入发生了变化,那么我们如何使用间谍来做到这一点呢?
我们可以给NewInvestmentView
写一个新的接受标准,在点击添加按钮时检查其表单是否正在提交:
describe("and when its add button is clicked", function() {
beforeEach(function() {
spyOnEvent(view.$el, 'submit');
view.$el.find('input[type=submit]').click();
});
it("should submit the form", function() {
expect('submit').toHaveBeenTriggeredOn(view.$el);
});
});
为了编写这个规范,我们使用 Jasmine jQuery 插件提供的spyOnEvent
全局函数。
它的工作原理是接受一个 DOM 元素(view.$el
)和一个我们想要监视的事件(submit
)。随后,我们可以通过toHaveBeenTriggeredOn
检查事件是否是在元素上触发的。
在这一章中,我们向您介绍了测试替身的概念,以及如何使用间谍软件对您的规范进行行为检查。
在下一章中,我们将看看如何使用假货和存根来替换我们规范的真正依赖,并加快它们的执行速度。