我有一些紧密耦合的遗留代码,我想用测试来覆盖。有时确保在另一个方法之前调用一个模拟方法很重要。一个简化的例子:
function PageManager(page) {
this.page = page;
}
PageManager.prototype.openSettings = function(){
this.page.open();
this.page.setTitle("Settings");
};
在测试中,我可以检查open()
和setTitle()
是否都被调用:
describe("PageManager.openSettings()", function() {
beforeEach(function() {
this.page = jasmine.createSpyObj("MockPage", ["open", "setTitle"]);
this.manager = new PageManager(this.page);
this.manager.openSettings();
});
it("opens page", function() {
expect(this.page.open).toHaveBeenCalledWith();
});
it("sets page title to 'Settings'", function() {
expect(this.page.setTitle).toHaveBeenCalledWith("Settings");
});
});
但setTitle()
仅在首次调用open()
后才能生效。我想检查第一个page.open()
是否被调用,然后是setTitle()
。我想写这样的东西:
it("opens page before setting title", function() {
expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle);
});
但Jasmine似乎没有内置这样的功能。
我可以这样做:
beforeEach(function() {
this.page = jasmine.createSpyObj("MockPage", ["open", "setTitle"]);
this.manager = new PageManager(this.page);
// track the order of methods called
this.calls = [];
this.page.open.and.callFake(function() {
this.calls.push("open");
}.bind(this));
this.page.setTitle.and.callFake(function() {
this.calls.push("setTitle");
}.bind(this));
this.manager.openSettings();
});
it("opens page before setting title", function() {
expect(this.calls).toEqual(["open", "setTitle"]);
});
这有效,但我想知道是否有一些更简单的方法来实现这一点。或者一些很好的方法来概括这一点,所以我不需要在其他测试中复制这段代码。
PS。当然正确的方法是重构代码以消除这种时间耦合。但是,它可能并不总是可能的,例如,与第三方库连接时。无论如何......我想首先用测试覆盖现有代码,尽可能少地修改它,然后再深入研究进一步的重构。
答案 0 :(得分:5)
试试这个:
it("setTitle is invoked after open", function() {
var orderCop = jasmine.createSpy('orderCop');
this.page.open = jasmine.createSpy('openSpy').and.callFake(function() {
orderCop('fisrtInvoke');
});
this.page.setTitle = jasmine.createSpy('setTitleSpy').and.callFake(function() {
orderCop('secondInvoke');
});
this.manager.openSettings();
expect(orderCop.calls.count()).toBe(2);
expect(orderCop.calls.first().args[0]).toBe('firstInvoke');
expect(orderCop.calls.mostRecent().args[0]).toBe('secondInvoke');
}
编辑:我刚刚意识到我的原始答案实际上与你在问题中提到的黑客一样,但是在设置间谍方面有更多的开销。使用你的" hack"它可能更简单。方式:
it("setTitle is invoked after open", function() {
var orderCop = []
this.page.open = jasmine.createSpy('openSpy').and.callFake(function() {
orderCop.push('fisrtInvoke');
});
this.page.setTitle = jasmine.createSpy('setTitleSpy').and.callFake(function() {
orderCop.push('secondInvoke');
});
this.manager.openSettings();
expect(orderCop.length).toBe(2);
expect(orderCop[0]).toBe('firstInvoke');
expect(orderCop[1]).toBe('secondInvoke');
}
答案 1 :(得分:3)
我想写这样的东西:
it("opens page before setting title", function() { expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle); });
但Jasmine似乎没有内置这样的功能。
看起来Jasmine人看过这篇文章,因为this functionality exists。我不知道它已经存在了多长时间 - 他们所有的API文档都回到了2.6提到它,尽管他们的档案旧版文档都没有提到它。
toHaveBeenCalledBefore(
expected
)
expect在另一个Spy之前调用的实际值(Spy)。<强>参数:强>
Name Type Description expected Spy Spy that should have been called after the actual Spy.
您示例的失败看起来像Expected spy open to have been called before spy setTitle
。
答案 2 :(得分:2)
为第二次调用创建一个假函数,该函数需要进行第一次调用
it("opens page before setting title", function() {
// When page.setTitle is called, ensure that page.open has already been called
this.page.setTitle.and.callFake(function() {
expect(this.page.open).toHaveBeenCalled();
})
this.manager.openSettings();
});
答案 3 :(得分:0)
使用间谍上的.calls.first()
和.calls.mostRecent()
方法检查特定的来电。
答案 4 :(得分:0)
基本上做了同样的事情。我有信心这样做,因为我用完全同步的实现模拟了函数行为。
it 'should invoke an options pre-mixing hook before a mixin pre-mixing hook', ->
call_sequence = []
mix_opts = {premixing_hook: -> call_sequence.push 1}
@mixin.premixing_hook = -> call_sequence.push 2
spyOn(mix_opts, 'premixing_hook').and.callThrough()
spyOn(@mixin, 'premixing_hook').and.callThrough()
class Example
Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
expect(@mixin.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
expect(call_sequence).toEqual [1, 2]
答案 5 :(得分:0)
最近我开发了一个名为strict-spies的茉莉花间谍的替代品,它解决了许多其他问题:
describe("PageManager.openSettings()", function() {
beforeEach(function() {
this.spies = new StrictSpies();
this.page = this.spies.createObj("MockPage", ["open", "setTitle"]);
this.manager = new PageManager(this.page);
this.manager.openSettings();
});
it("opens page and sets title to 'Settings'", function() {
expect(this.spies).toHaveCalls([
["open"],
["setTitle", "Settings"],
]);
});
});