Test node-schedule with sinon FakeTimers - javascript

I want to test my scheduleJob from the node-schedule package. With sinon useFakeTimers() i can skip the time. Unfortunately my scheduler doesn't seem to 'believe' the fake time. When i set the scheduler to 1 minute it works perfectly, so i know that it works. I also tried to set the fake time just a minute before the call, also doesn't work.
Sinon:
let clock = sinon.useFakeTimers(moment().toDate());
clock.tick(60*60*23);
And my scheduledJob:
let job_2 = schedule.scheduleJob(new Date(date.toISOString()), function (user) {
console.log("get's here..");
if (user.status === 'pending') {
user.remove(function (error) {
if (!error) {
mid.addEvent({
action: 'deleted_user'
}, {
name: user.name
}, function (error) {
if (error) {
console.log("error: " + error);
}
});
}
});
}
}.bind(null, user));
Has anyone any idea?

#MPasman
What does your test look like? The node-schedule authors test their code with sinon so I don't see why this would be an issue.
see this for examples
I was able to test my job in angular 6 using jasmine's fakeAsync like so:
it('should call appropriate functions when cronJob is triggered, bad ass test',
fakeAsync(() => {
const channelSpy = spyOn(messengerService, 'createChannels');
const listenerSpy = spyOn(messengerService, 'createListener');
const messengerSpy = spyOn(messengerService.messenger,
'unsubscribeAll');
// reset your counts
channelSpy.calls.reset();
listenerSpy.calls.reset();
messengerSpy.calls.reset();
messengerService.cronJob.cancel();
// run cron job every second
messengerService.cronJobTime = '* * * * * *';
messengerService.scheduleMyJsCronJob();
tick(3150);
messengerService.cronJob.cancel();
expect(channelSpy.calls.count()).toEqual(3);
expect(listenerSpy.calls.count()).toEqual(3);
expect(messengerSpy.calls.count()).toEqual(3);
}));
The actual function in the service I am testing:
scheduleMyJsCronJob(): void {
this.cronJob = scheduleJob(this.cronJobTime, () => {
// logic to unsubscribe at midnight
this.messenger.unsubscribeAll();
// create new channels
this.createChannels(
this.sessionStorageService.getItem('general-settings'));
this.createListener(
this.sessionStorageService.getItem('general-settings'));
});
}
The basic strategy is:
1) spy on functions that your cronJob should call when scheduled
2) cancel all previous Jobs if any (you could also do this in an afterEach, which runs after each unit test). Node-schedule also offers a function called scheduledJobs which returns an object with all the functions scheduled. You can loop over it and cancel them all)
3) set the cronTime to run every second for easier testing
4) tick the clock a little over a second (in my case i did a little over 3 seconds)
5) cancel the job to stop it (otherwise it will keep running and your test will timeout).
6) expect your function(s) to be called x amount of times
Hope this helps you.

Basically sinon.useFakeTimers() method replaces setInterval and setTimeout asynchronous methods with a built in synchronous methods that you can control using clock.tick()
clock.tick(time) method invokes the replaced synchronous methods and basically forwards the time by time specified.
node-schedule on the other hand is a cron-like job scheduler so it doesn't use setInterval and setTimeout
https://www.npmjs.com/package/node-schedule
https://sinonjs.org/releases/v1.17.6/fake-timers/
Hope this makes it a little more clear

Related

Angular 2, throttleTime on observable from subject

Im trying to make throttleTime take effect, but for some reason it does not kick in. I have the following:
// Class Properties
private calendarPeriodSubject: Subject<x> = new Subject<x>();
private calendarPeriodObservable$ = this.calendarPeriodSubject.asObservable();
// Throttling fails here (Inside constructor):
const calendarPeriodSubscription = this.calendarPeriodObservable$.pipe(throttleTime(750)).subscribe(async (calendar: x) => {
// Do http stuff here
}
});
The subject gets called like this:
this.calendarPeriodSubject.next(x);
I also tried with:
this.calendarPeriodSubject.pipe(throttleTime(1000)).subscribe({next: (x) => x});
I would like to process the FIRST time, and the following clicks should not have any effect before after ieg 750ms - To prevent the server from getting spammed basically.
Anyone has any idea?
Thanks!
The problem is that you are using the wrong operator for your use case. The way I understand your explanation you want to send through your first call and stop any further calls to your Server for some amount of ms. But what throttleTime(sec) does is simply put a timer on the action and execute it sec ms later. So you server will still be spammed, just a few ms later.
Your case screams debounceTime() for me. debounceTime docu
This disables any further data to be passed though the Observable for the specified time after a value has been emitted.
Therefore your code should be fine if you use something like:
const calendarPeriodSubscription =
this.calendarPeriodObservable$.pipe(debounceTime(750)).subscribe((calendar: x) => {
// Stuff with returned data
});

How to make sure that a method is called only once given specific parameters

i have a class called Feature and it contains the following methods setUser(boolean),execute(), doExecute()
And according to the below stated parameters, when i call execute() method, doExecute() method should be called only once.
I tried to test that doExecute() method is called only once in the below code using sinon, but I receive an error message says: doExecute() method is called zero times.
please let me know how to check correctly if doExecute() is called exactly once
code:
t.context.clock = sinon.useFakeTimers();
const domain = 'testDomain';
const delayInMillis = 0;
const delayInSecs = delayInMillis / 1000;
const feature = new Feature(domain, delayInMillis);
feature.setUser(false);
const p = feature.execute()
.then(() => sinon.spy(feature.doExecute()))
.then(() => t.pass());
sinon.assert.callCount(sinon.spy(feature.doExecute()),1);
t.context.clock.restore();
return p;
});
First of all, sinon.spy() accepts a function as a parameter, and you're passing the result of your function.
// wrong
sinon.spy(feature.doExecute());
// correct
sinon.spy(feature.doExecute);
Next, you need to store your spy into a variable for later reference. In your code you're just creating new spies every time.
const featureDoExecuteSpy = sinon.spy(feature.doExecute);
// some code
sinon.assert.callCount(featureDoExecuteSpy, 1);
There are quite a few problems with this.
As sameeh pointed out, you're simply creating a new spy as part of your assertion. Obviously a new spy will have a call count of zero. You need to make sure the assertion is referring to the same spy instance as will be called by the code under test.
A crucial thing that sameeh missed, however, is that the spy has to be passed through to your code under test. This is why the usual form of `sinon.spy()' accepts an object followed by a method name. It replaces the method on that object with a spy that wraps the original:
// wrong
sinon.spy(feature.doExecute());
// still wrong
sinon.spy(feature.doExecute);
// correct
sinon.spy(feature, 'doExecute');
You can then access the spy in place on that object for your assertion. You don't need to store it in a local variable:
sinon.assert.callCount(feature.doExecute, 1);
Another problem: Your assertion is not waiting for the promise from feature.execute to resolve. This means that if doExecute is being invoked after some asynchronous operation within execute, your assertion is happening too early. It thus needs to be in a then after the others like so (note that this code still won't work due to other problems I'll address in a bit):
const p = feature.execute()
.then(() => sinon.spy(feature, 'doExecute'))
.then(() => t.pass())
.then(() => sinon.assert.callCount(feature.doExecute,1));
More problems... Your use of fake timers is very strange, and makes it difficult for me to tell whether or not the promise returned by feature.execute() will ever resolve.
If it needs the timer to tick in order to resolve? It won't. Because you aren't ever ticking the timer. I don't know what t.pass() does, but since it is chained onto the promise returned by feature.execute(), it will never get called if you don't tick the timer somewhere else. Nor will your code that creates your spy, for that matter-- for the same reason.
You need to create your spy before invoking feature.execute(), and probably invoke t.pass() after, if it is indeed the method that ticks your fake timer:
sinon.spy(feature, 'doExecute')
const p = feature.execute()
.then(() => sinon.assert.callCount(feature.doExecute,1));
t.pass();
Finally, I don't know what test framework you're using, but you generally want to restore fake timers and other global state in blocks that will always execute, whether or not your test is successful. This ensures that failed tests don't leave crap hanging around that will carry over into other tests. Mocha has methods beforeEach and afterEach for this purpose:
beforeEach(function() {
t.context.clock = sinon.useFakeTimers();
});
afterEach(function() {
t.context.clock.restore()
});
it('whatever your test name is', function() {
const domain = 'testDomain';
const delayInMillis = 0;
const delayInSecs = delayInMillis / 1000;
const feature = new Feature(domain, delayInMillis);
sinon.spy(feature, 'doExecute')
const p = feature.execute()
.then(() => sinon.assert.callCount(feature.doExecute,1));
t.pass();
return p;
});

How can I make Protractor NOT wait for $timeout?

I'm testing my angular application with Protractor.
Once the user is logged in to my app, I set a $timeout to do some job in one hour (so if the user was logged-in in 13:00, the $timeout will run at 14:00).
I keep getting these failures:
"Timed out waiting for Protractor to synchronize with the page after 20 seconds. Please see https://github.com/angular/protractor/blob/master/docs/faq.md. The following tasks were pending: - $timeout: function onTimeoutDone(){....."
I've read this timeouts page: https://github.com/angular/protractor/blob/master/docs/timeouts.md
so I understand Protractor waits till the page is fully loaded which means he's waiting for the $timeout to complete...
How can I make Protractor NOT wait for that $timeout?
I don't want to use:
browser.ignoreSynchronization = true;
Because then my tests will fail for other reasons (other angular components still needs the time to load...)
The solution will be to flush active timeouts (as #MBielski mentioned it in comments), but original flush method itself is available only in anuglar-mocks. To use angular-mocks directly you will have to include it on the page as a <script> tag and also you'll have to deal with all overrides it creates, it produces a lot of side effects. I was able to re-create flush without using angular-mocks by listening to any timeouts that get created and then reseting them on demand.
For example, if you have a timeout in your Angular app:
$timeout(function () {
alert('Hello World');
}, 10000); // say hello in 10 sec
The test will look like:
it('should reset timeouts', function () {
browser.addMockModule('e2eFlushTimeouts', function () {
angular
.module('e2eFlushTimeouts', [])
.run(function ($browser) {
// store all created timeouts
var timeouts = [];
// listen to all timeouts created by overriding
// a method responsible for that
var originalDefer = $browser.defer;
$browser.defer = function (fn, delay) {
// originally it returns timeout id
var timeoutId = originalDefer.apply($browser, arguments);
// store it to be able to remove it later
timeouts.push({ id: timeoutId, delay: delay });
// preserve original behavior
return timeoutId;
};
// compatibility with original method
$browser.defer.cancel = originalDefer.cancel;
// create a global method to flush timeouts greater than #delay
// call it using browser.executeScript()
window.e2eFlushTimeouts = function (delay) {
timeouts.forEach(function (timeout) {
if (timeout.delay >= delay) {
$browser.defer.cancel(timeout.id);
}
});
};
});
});
browser.get('example.com');
// do test stuff
browser.executeScript(function () {
// flush everything that has a delay more that 6 sec
window.e2eFlushTimeouts(6000);
});
expect(something).toBe(true);
});
It's kinda experimental, I am not sure if it will work for your case. This code can also be simplified by moving browser.addMockModule to a separate node.js module. Also there may be problems if you'd want to remove short timeouts (like 100ms), it can cancel currently running Angular processes, therefore the test will break.
The solution is to use interceptors and modify the http request which is getting timeout and set custom timeout to some milliseconds(your desired) to that http request so that after sometime long running http request will get closed(because of new timeout) and then you can test immediate response.
This is working well and promising.

Passing a non-standard Scheduler to an operator

Let's say that I want to pass a Scheduler to an RxJS operator that makes it emit notifications every 5 seconds. Of course, this is very easy to do by just using interval or other existing operators. But if I really want to use a scheduler to accomplish that, how would I go about it?
My first thought is to subclass Rx.Scheduler.default. Would that be the way to go? And if so, how could that subclass look? Again, I understand that this is a complicated way to accomplish something that's easy using operators, but I am just curious about custom schedulers.
Operations should always be independent of the Schedulers that are used to implement them. Schedulers only know about one thing, time. Every scheduler is specifically built to deal with its own notion of time. They are expressly not built to handle specific operators since that would be a conflation of concerns.
So for your stated goal of creating a recurring task, I wouldn't recommend trying to actually create your own scheduler, it simply isn't needed. Schedulers come with an interface that already supports this.
You can use either the schedulePeriodic or the scheduleRecursiveFuture to accomplish this.
//Using periodic
Rx.Observable.interval = function(period, scheduler) {
return Rx.Observable.create(function(observer) {
return scheduler.schedulePeriodic(0, period, function(count) {
observer.onNext(count);
return count + 1;
});
});
};
//Using scheduleRecursive
Rx.Observable.interval = function(period, scheduler) {
return Rx.Observable.create(function(observer) {
return scheduler.scheduleRecursiveFuture(0, period, function(count, self) {
observer.onNext(count);
self(period, count + 1);
});
});
};
Reference 1,
Reference 2;
The former should be easier to wrap your head around, essentially it is just scheduling something to occur repeatedly spaced in time based on the period parameter.
The latter is usually a little more difficult to explain, but essentially you are scheduling a task and then sometime during the execution of that task you are rescheduling it (which is what the self parameter) is doing. This allows you do get the same effect using the period parameter.
The timing of this work is all directly affected by which scheduler you decide to pass into the operator. For instance, if you pass in the default it will try to use the best method for an asynchronous completion, whether that be setTimeout, setInterval or some other thing I can't remember. If you pass in a TestScheduler or a HistoricalScheduler this actually won't do anything until you increment each of their respective clocks, but doing so gives fine grained control over how time flows.
tl;dr Only implement new Schedulers if you have some new overall notion of time to express, otherwise use the existing API to do work on whatever Scheduler best fits how you want time to pass.
Should you roll your own?
Plainly: No. Most likely you can get done what you need done with an existing operator. Something like buffer, window, sample, etc. Scheduler development is not completely straightforward.
How to roll your own RxJS 4 Scheduler
If you want to implement your own Scheduler, in RxJS 4, you'd subclass Rx.Scheduler, then override each schedule method: schedule, scheduleFuture, schedulePeriodic, scheduleRecursive, scheduleRecursiveFuture... You'd also likely want to override now to return something relevant to your schedule.
Here is an example of a custom scheduler that uses button clicks inside of real time
/**
NOTE: This is REALLY fast example. There is a lot that goes into implementing a
Scheduler in RxJS, for example what would `now()` do in the scheduler below? It's also missing a number of scheduling methods.
*/
class ButtonScheduler extends Rx.Scheduler {
/**
#param {string} the selector for the button (ex "#myButton")
*/
constructor(selector) {
super();
this.button = document.querySelector(selector);
}
schedule(state, action) {
const handler = (e) => {
action(state);
};
const button = this.button;
// next click the action will fire
button.addEventListener('click', handler);
return {
dispose() {
// ... unless you dispose of it
button.removeEventListener('click', handler);
}
};
}
// Observable.interval uses schedulePeriodic
schedulePeriodic(state, interval, action) {
const button = this.button;
let i = 0;
const handler = (e) => {
const count = i++;
if(count > 0 && count % interval === 0) {
state = action(state);
}
};
// next click the action will fire
button.addEventListener('click', handler);
return {
dispose() {
// ... unless you dispose of it
button.removeEventListener('click', handler);
}
};
}
}
Rx.Observable.interval(1, new ButtonScheduler('#go'))
.subscribe(x => {
const output = document.querySelector('#output');
output.innerText += x + '\n';
});
How to do it in RxJS 5 (alpha)
Scheduling changed again in RxJS 5, since that version was rewritten from the ground up.
In RxJS5, you can create any object that adheres to the following interface:
interface Scheduler {
now(): number
schedule(action: function, delay: number = 0, state?: any): Subscription
}
Where Subscription is just any object with an unsubscribe function (same as dispose, really)
Once again, though, I don't advise creating a scheduler unless it's completely necessary.
I really hope that helps answer your question.

Heartbeat monitor and unit testing it in javascript - how to?

I have a 'service module' that listens for periodic heartbeats (over socket.io) and then if a heartbeat is missed publishes an event on an event bus (Backbone.Events). Also if the heart beat resumes later, it publishes an event.
My unit test looks something like this:
describe('Heartbeat service', function() {
var HeartbeatService;
var heartbeatInterval = 1;
//the server is expected to send heartbeats FASTER actually.
//otherwise we risk false alarms
jasmine.require(['services/HeartbeatService'], function(Service) {
HeartbeatService = Service;
});
/*Since these tests use setInterval, it'd be erroneous to allow
mocks to be overwritten by another test. Thus not using beforeEach()*/
function createMocks(){
var mockEventAggregator = jasmine.createSpyObj('Events',['trigger']);
return {
target : new HeartbeatService(heartbeatInterval, mockEventAggregator),
ea : mockEventAggregator
};
}
it('should raise an event on the FIRST missed beat', function() {
var mocks = createMocks();
mocks.target.start();
setTimeout(function(){
expect(mocks.ea.trigger).toHaveBeenCalled();
}, 2);
});
//..other similar tests
});
My dislike for this test is that if it fails then it is likely that the reporting tool will list the failures under a different test - since the actual assertion will occur outside the it() function.
You could use SinonJSs fake timer to set the clock 2 ticks forward instead of using setTimeout.

Categories

Resources