我有一些紧密耦合的遗留代码,我想用测试来覆盖.有时确保在另一个方法之前调用一个模拟方法很重要.一个简化的例子:
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","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.当然,正确的方法是重构代码以消除这种时间耦合.但是,它可能并不总是可能的,例如,与第三方库连接时.无论如何…我想首先用测试覆盖现有代码,尽可能少地修改它,然后再深入研究进一步的重构.
解决方法
试试这个:
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'); }
编辑:我刚刚意识到我的原始答案实际上与你在问题中提到的黑客一样,但是在设置间谍方面有更多的开销.使用“黑客”方式进行操作可能更简单:
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'); }