Testing callback called after a event trigger with Sinon Js - javascript

It's my first test on Javacript with Mocha/Sinon/Chai And I don't know if it's possible to do this :
var obj = {
first : function () {
console.log('make job 1');
}
};
var objManager = function() {
$(document).on('event1', obj.first);
};
new objManager();
var spy = sinon.spy(obj, 'first');
describe('Test', function () {
it('My first test', function () {
$(document).trigger('event1');
spy.should.not.have.been.called;
});
});
My spy isn't called and don't understand why... My function "obj.first" has printed "make job 1".
if I modify my test by :
it('My first test', function () {
obj.first();
spy.should.not.have.been.called;
});
My spy is called.
So my question is : How make sinon spy work with a event ?

The problem is that you first bind the function to the event and then replace the function in obj with the spy. Doing this will not have any effect on the function you have bound to the event cause this is still the original function.
Do test this you have to create the spy before instantiate your objManager.

Related

How to check if a method is called before another one got called

I have a method called mainMethod() and it returns a promise.
This method contains several methods m1(), m2()...,m5().
Now I am making a unit test using sinon,
I want to check if m1() is called and m2() is not called.
Because I have an array that not empty after m1() is called but it will be empty after m2() is called.
I want to make a check or test after m1() is called and before m2() is called.
Is it possible using sinon?
Yes, using Sinon is possible. It has calledAfter and calledBefore.
http://sinonjs.org/releases/v6.1.3/spies/
For example
it('some testing', function() {
var m1 = { method: function () {} };
var m2 = { method: function () {} };
var spyM1 = sinon.spy(m1, "method");
var spyM2 = sinon.spy(m2, "method");
m1.method(42);
m2.method(1);
assert(spyM1.calledBefore(spyM2));
assert(spyM2.calledAfter(spyM1));
});

How to define a sub method using "prototype"?

When I define a javascript class with methodes and submethodes this way it works:
function Controller () {
this.OrdersSyncFreq = 3000; //ms
this.syncOrders = function () {};
this.syncOrders.start = function () { console.log("start was called"); };
this.syncOrders.stop = function () { console.log("stop was called"); };
}
But how can I define the function Controller.syncOrders.start() later using "prototype"? Something like this does not work:
Controller.prototype.syncOrders.stop = function () {
console.log("The NEW stop was called");
}
Looking a bit more around, i found out it can be written like this:
function Controller () {
this.OrdersSyncFreq = 3000; //ms
this.syncOrders(); // have to be called at init to make sure the definitions of start and stop will be active.
}
Controller.prototype.syncOrders = function () {
this.syncOrders.start = function () {
console.log("Start was called, frequence is: ",this.OrdersSyncFreq);
}.bind(this); // .bind(this) is needed to have access this of your controller instance
this.syncOrders.stop = function () {
console.log("Stop was called, frequence is: ",this.OrdersSyncFreq);
};
}
// run the code
var app = new Controller();
app.syncOrders.start(); // console: Start was called, frequence is: 3000
app.syncOrders.stop(); // console: Stop was called, frequence is: undefined
The methodes of syncOrders - Start() and Stop() do not need to be prototyped, because syncOrders will not instantiated.
Anyhow I'm not sure if it really make sense to do it like this. I did it only because of the namespacing. may be it is better to use something more simple like syncOrdersStart() and syncOrdersStop() instead.

Sinon Spy to check function has been called

I am trying to use sinon.spy() to check that a function has been called. The function is called getMarketLabel and it returns marketLabel and accepts it into the function. I need to check that getMarketLabel has been called. I actually call getMarketLabel in one place, like so:
{getMarketLabel(sel.get('market'))}
The code I have so far is:
describe('Check if it has been called', () => {
let spy;
beforeEach(() => {
spy = sinon.spy(getMarketLabel, 'marketLabel');
})
it('should have been called', () => {
expect(spy).to.be.calledWith('marketLabel');
});
});
This is the error I receive:
TypeError: Attempted to wrap undefined property marketLabel as function
Sinon can't spy on functions that aren't a property of some object, because Sinon has to be able to replace the original function getMarketLabel by a spied-on version of that function.
A working example:
let obj = {
getMarketLabel(label) {
...
}
}
sinon.spy(obj, 'getMarketLabel');
// This would call the spy:
obj.getMarketLabel(...);
This syntax (which is close to what you're using) also exists:
let spy = sinon.spy(getMarketLabel);
However, this only triggers the spy code when explicitly calling spy(); when you call getMarketLabel() directly, the spy code isn't called at all.
Also, this won't work either:
let getMarketLabel = (...) => { ... }
let obj = { getMarketLabel }
sinon.spy(obj, 'getMarketLabel');
getMarketLabel(...);
Because you're still calling getMarketLabel directly.
This is the error I receive: TypeError: Attempted to wrap undefined
property marketLabel as function
You need to require the helper.js into your test file, then replace the relevant method on the required module and finally call the method replaced with the spy:
var myModule = require('helpers'); // make sure to specify the right path to the file
describe('HistorySelection component', () => {
let spy;
beforeEach(() => {
spy = sinon.stub(myModule, 'getMarketLabel'); // replaces method on myModule with spy
})
it('blah', () => {
myModule.getMarketLabel('input');
expect(spy).to.be.calledWith('input');
});
});
You cannot test whether the spy is called with helpers.sel('marketLabel') as this function will be executed before the test is conducted. You will therefore by writing:
expect(spy).to.be.calledWith(helpers.sel('marketLabel'));
be testing that that the spy is called with whatever value returned by helpers.sel('marketLabel') (which is undefined by default).
The content of helper.js should be:
module.exports = {
getMarketLabel: function (marketLabel) {
return marketLabel
}
}

Revealing Module Alternative or Variation for Testing

Overview
I've been using the Revealing Module Pattern for a few months and I'm looking for an alternative or variation on this pattern that will solve both issues I'm currently having with event handlers and testability. I know I could come up with some combination of what I have below to solve my problem, but I'm hoping to find a clean alternative that I could use consistently that addresses both of my current concerns.
Revealing Module Pattern
So in this example, I have no issues with event handlers, but I can't mock calls to functions
within functions to test in isolation:
var Lion = (function () {
// Reference to rawr works as expected when the click event is triggered
function watch() {
document.addEventListener('click', rawr);
}
function rawr() {
console.log('rawr');
}
function attack() {
console.log('attack');
}
/*
* Can't test goCrazy() in isolation. Mocking rawr() and attack()
* has no effect as demonstrated below.
*/
function goCrazy() {
rawr();
attack();
// Important "crazy" logic
}
return {
watch: watch,
rawr: rawr,
attack: attack,
goCrazy: goCrazy
};
}());
module.exports = Lion;
Example Test Case (Jasmine)
describe('Mock Check', function () {
it('should mock rawr() and attack() and test only goCrazy logic', function () {
var lion = require('Lion');
spyOn(lion, 'rawr').and.reutrnValue(true);
spyOn(lion, 'attack').and.reutrnValue(true);
var crazy = lion.goCrazy();
expect(lion.rawr).toHaveBeenCalled(); // <-- Fails
expect(lion.attack).toHaveBeenCalled(); // <-- Fails
// can't test goCrazy() logic in isolation :(
});
});
Same Module using this instead and invoked using new
In this example, I can successfully mock calls to functions within functions, but if I attempt to add an event handler, this becomes undefined when the event is triggered.
var Lion = function () {
// Reference to 'this' becomes undefined when event is triggered
this.watch = function () {
document.addEventListener('click', this.rawr);
}
this.rawr = function () {
console.log('rawr');
}
this.attack = function () {
console.log('attack');
}
/*
* Can successfully test goCrazy() in isolation by being able to mock
* rawr() and attack() as needed
*/
this.goCrazy = function () {
this.rawr();
this.attack();
// Important "crazy" logic
}
};
module.exports = Lion;
Example Test Case (Jasmine)
describe('Mock Check', function () {
it('should mock rawr() and attack() and test only goCrazy logic', function () {
var Lion = require('Lion');
var lion = new Lion();
spyOn(lion, 'rawr').and.reutrnValue(true);
spyOn(lion, 'attack').and.reutrnValue(true);
var crazy = lion.goCrazy();
expect(lion.rawr).toHaveBeenCalled(); // <-- Success
expect(lion.attack).toHaveBeenCalled(); // <-- Success
// testing goCrazy logic in isolation :)
});
});
Thanks for your time. If any clarification is necessary, let me know and I'll modify my post.
The actual problem here is that, the event handler loses the context of the current object. You can bind it like this
document.addEventListener('click', this.rawr.bind(this));
This will make sure that whenever the rawr is invoked, the this inside rawr corresponds to the lion object which you created.

jasmine Expected spy myLinks to have been called error

I am having a hard time understanding jasmine spyOn function.
I wrote a simple function and test if my method was called:
function myView() {
myLinks();
}
Here are my tests:
describe('#myView', function() {
it('updates link', function() {
var spyEvent = spyOn(window, 'myLinks');
expect(spyEvent).toHaveBeenCalled();
});
});
This returns the following failure:
Expected spy myLinks to have been called
What am i doing wrong here?
You need to call the myView() function so the myLinks() have been called.
function myLinks(){
//some tasks
}
function myView() {
myLinks();
}
This two function above are declared in window object, then you create a spy object pointing to the window.
describe('#myView', function() {
myView();//Call the method so the myLinks was called too
it('updates link', function() {
var spyEvent = spyOn(window, 'myLinks');
expect(spyEvent).toHaveBeenCalled();
});
});

Categories