How to check that one function is called before another

I have a pretty closely related old code that I want to cover with tests. It is sometimes important to ensure that one wearing method is called before the other. A simplified example:

function PageManager(page) { this.page = page; } PageManager.prototype.openSettings = function(){ this.page.open(); this.page.setTitle("Settings"); }; 

In the test, I can verify that both open() and 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"); }); }); 

But setTitle() will only work after the first call to open() . I would like to verify that the first page.open() is called and then setTitle() . I would like to write something like this:

 it("opens page before setting title", function() { expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle); }); 

But Jasmine does not seem to have such functionality.

I can hack something like this:

 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"]); }); 

This works, but I wonder if there is an easier way to achieve this. Or some good way to generalize this, so I won’t need to duplicate this code in other tests.

PS. Of course, the right way is to reorganize the code to eliminate this temporary connection. This may not always be possible, for example, when interacting with third-party libraries. In any case ... I would like to first examine the existing code with the help of tests, changing it as little as possible before delving into further refactoring.

+6
source share
6 answers

Try the following:

 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'); } 
+3
source

I would like to write something like this:

 it("opens page before setting title", function() { expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle); }); 

But Jasmine does not seem to have such functionality.

It seems that the Jasmine people saw this post because this functionality exists . I'm not sure how long it has been - all of their API documents prior to 2.6 mention this, although none of their old-style archival documents mention this.

toHaveBeenCalledBefore ( expected )
expect the actual value (a Spy ) that was called before another Spy .

Options:

 Name Type Description expected Spy Spy that should have been called after the actual Spy. 

The error for your example looks like Expected spy open to have been called before spy setTitle .

+2
source

Create a fake function for the second call that expects the first call to be made

 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(); }); 
+2
source

Inspect specific calls using .calls.first() and .calls.mostRecent() on a spy.

0
source

Basically did the same thing. I was sure of this because I mocked the behavior of a function with fully synchronous implementations.

 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] 
0
source

Recently, I have developed a Jasmine spy replacement called strict-spies that solves this problem among many others:

 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"], ]); }); }); 
0
source

Source: https://habr.com/ru/post/977466/


All Articles