Spy on setTimeout and clearTimeout in karma and jasmine

I can't seem to keep track of setTimeout and clearTimeout in Jasmine tests that go through karma.

I tried options on all of these

 spyOn(window, 'setTimeout').and.callFake(()=>{}); spyOn(global, 'setTimeout').and.callFake(()=>{}); spyOn(window, 'clearTimeout').and.callThrough(); clock = jasmine.clock(); clock.install(); spyOn(clock, 'setTimeout').and.callThrough(); runMyCode(); expect(window.setTimeout).toHaveBeenCalled(); // no expect(global.setTimeout).toHaveBeenCalled(); // nope expect(window.clearTimeout).toHaveBeenCalled(); // no again expect(clock.setTimeout).toHaveBeenCalled(); // and no 

In each case, I can confirm that setTimeout and clearTimeout were called in runMyCode , but instead I always get Expected spy setTimeout to have been called.

For window , obviously, this is because the test and the runner (the Karma window) are in different frames (so why should I expect something else). But because of this, I see no way to confirm that these global functions have been called.

I know that I can use jasmine.clock() to confirm that the timeout / interval callback is being called, but it looks like I can't watch setTimeout myself. And confirmation that clearTimeout was called simply is not possible.

At this point, I can only add a separate abstraction layer to wrap setTimeout and clearTimeout or add functions as dependencies that I did before, but I think this is weird.

+5
source share
2 answers

The only solution I could find was to use Rewire (in my case, I also need to use Rewire-Webpack ).

Rewire allows you to replace global methods, but once a method has been replaced, it cannot be used. So, in order to successfully use toHaveBeenCalledWith , you have to wrap and proxy the mock function.

 var rewire = require('rewire'), myModule = rewire('./path/to/module'); describe(function () { var mocks = { setTimeout: function () { return 99: }, clearTimeout: function () {} }; beforeEach(function () { // This will work myModule.__set__('setTimeout', function () { mocks.setTimeout.apply(null, arguments) }) // This will NOT work myModule.__set__('clearTimeout', mocks.clearTimeout) }); it('calls setTimeout', function () { spyOn(mocks, 'setTimeout').and.callThrough(); spyOn(mocks, 'clearTimeout').and.callThrough(); myModule.doSomething(); // this will invoke setTimeout locally expect(mocks.setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 1000); expect(mocks.clearTimeout).toHaveBeenCalledWith(99); // Won't work (see above) }); }); 

Naturally, this will certainly stop working the next time Jasmine, Rewire, Karma, Webpack ... or the weather ... changes (grrr). If this does not work for you, leave a comment so that future developers know.

0
source

I managed to get it to work as follows:

 spyOn(window, 'setTimeout'); runMyCode(); expect(setTimeout).toHaveBeenCalled(); 

Just remove the 'window' object from the setTimeout call.

+1
source

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


All Articles