Spy on functions without objects - javascript

Im testing a publish method on a pub sub class. I am creating a callback function within the beforeEach function and subscribing to the class. In the it method I am publishing the event and attempting to test that the callback was called which is basically how the class works. I have got the test working and it passes but the problem is I had to use a setTimeout to get this to work. I believe this is probably not the right way to do this.
describe('publish', () => {
let testpublish;
let callback;
beforeEach(() => {
callback = function(data) { return data + 10; }
testpublish = {
'id': 'testpublish1',
'event': 'testpublish',
'callback': callback
};
subject.subscribe(testpublish);
});
it('should call the subscription function', () => {
subject.publish('testpublish', 9);
setTimeout(() => {
expect(callback).toEqual(19);
});
});
});
I initially wanted to spy on the callback just to see if it was called but the documentation for Jasmine says I must place my method in an object:
spyOn(obj, methodName) → {Spy}
Any advice on a better way to do this would be appreciated. Thanks.
PubSub Class if useful ??
#Injectable()
export class Pubsub {
private events: any = {};
public subscribe(config: any) {
let event = config['event'];
this.events[event] = this.events[event] || [];
if (this.events[event].length < 1) {
this.events[event].push(config);
} else {
for (let i = 0; i < this.events[event].length; i++) {
if (this.events[event][i].id !== config.id) {
this.events[event].push(config);
}
}
}
}
public unsubscribe(obj: Object) {
let event = obj['event'];
let id = obj['id'];
if (this.events[event]) {
this.events[event] = this.events[event].filter((eventObj) => {
return eventObj.id !== id;
});
}
if (this.events[event].length === 0) {
delete this.events[event];
}
}
public publish(event: string, data: any) {
if (this.events[event]) {
this.events[event].forEach(function(obj) {
obj.callback(data);
});
}
}
public getEvents() {
return this.events;
}
}

Existing function cannot be spied, because a spy is a new function, and the reference to original function is already used in the place where it is being called.
Considering that callback function is defined in the test itself, not inside the application, it should be defined as a spy in the first place:
callback = jasmine.createSpy();
It doesn't even have to do something because its return value doesn't add value to the test.
And it is tested like
const arg = {};
subject.publish('testpublish', arg);
expect(callback.calls.count()).toBe(1);
expect(callback.calls.first().args[0]).toBe(arg);
publish is synchronous, as well the rest of the class. There's no need for setTimeout, and it is harmful here. When done parameter isn't specified for the test, it is considered synchronous, and setTimeout makes assertions ignored in this test.
This
it('should pass', () => {
setTimeout(() => {
expect(1).toBe(2);
});
});
will always pass. And only if if the suite has no other tests, this will trigger SPEC HAS NO EXPECTATIONS warning.

jasmine.createSpy('spy') will do work.
describe('publish', () => {
let testpublish;
let callback;
let subject = new Pubsub();
beforeEach(() => {
callback = function (data) {
return data + 10;
}
testpublish = {
'id': 'testpublish1',
'event': 'testpublish',
'callback': jasmine.createSpy('spy')
};
subject.subscribe(testpublish);
});
it('should call the subscription function', () => {
subject.publish('testpublish', 9);
expect(testpublish.callback).toHaveBeenCalledWith(9);
});
});

Related

Jest spy not working while testing a function within a function

I'm trying to test a function in a class-based entity and trying to spy on another function that is being called inside the one I'm testing. But even though the child function is being called once, Jest fails to spy on it and says it was called 0 times.
Let's say this is my class:
class Example {
function1() {
const data = this.function2();
return data;
}
function2() {
...some code
}
}
So to test function1, I'm doing this:
describe('ExampleComponent', () => {
beforeEach(() => {
client = new Example();
});
it('should call function2 once', async() => {
const callFunction1 = client.function1();
const function2Spy = jest.spyOn(client, 'function2');
expect(function2Spy).toHaveBeenCalledTimes(1);
});
});
What am I missing here?
You are calling function first then spying on another function.
Try spying before function call
describe('ExampleComponent', () => {
beforeEach(() => {
client = new Example();
});
it('should call function2 once', async() => {
const function2Spy = jest.spyOn(client, 'function2');
client.function1();
expect(function2Spy).toHaveBeenCalledTimes(1);
});
});

How to use jest to test a function's return value(array)?

Here is my function to recursively loop through a object:
const helper = (obj,list) =>{
for (var property in obj){
if(obj.hasOwnProperty(property)) {
if(typeof obj[property] == "object") {
helper(obj[property],list);
}else {
if (property === "$ref"){
if(!list.includes(obj[property])){
list.push(obj[property]);
}
}
}
}
}
return list;
};
The object is simple, please see below:
{
"person": {
"properties": {
"sex":{
"type":"string"
},
"name": {
"$ref": "#/person/properties/sex"
}
}
}
}
The the helper will finally return a list ['#/person/properties/sex']
To run the code, just do helper(some_obj,[])
Here is my jest test code:
describe('helper function test', () =>{
it('should return a list', () =>{
const mock = jest.fn((obj,[]) => helper(obj,[]));
const list = mock(obj,[]);
expect(list).toMatch(['something']);
});
});
I have also tried:
describe('helper function test', () =>{
it('should return a list', () =>{
const list = helper(obj, []);
expect(list).toMatch(['something']);
});
});
The jest tells me the expect object is a array but it's value is [], which means empty. Actually I did a manually test for helper function in the function file, the return has no problem, which is what I expect.
One thing to mention, I use this helper inside of a promise later, I do not know the issue is related to promise, since the promise function has not called. I even tried to comment out the promise function, still no luck.
Would someone tells me how to get the real result from the jest? I would really appreciate any helps here! Thank you for your time.
I don't think you need to use jest.fn in here. If you want to test your function, you can do this:
describe('helper function test', () =>{
it('should return a list', () =>{
const list = helper(obj, []);
expect(list).toEqual(['something']);
});
});

Angular Observables return anonymous function

I have this method that I copied from a websocket tutorial but I don't understand the meaning of the "return () => { ... }" inside the observable ? Can someone explain me what is the purpose of that ?
public onMessage(topic: string, handler = SocketClientService.jsonHandler) : Observable<any> {
return this.connect().pipe(first(), switchMap(client => {
return new Observable<any>(observer => {
const subscription : StompSubscription = client.subscribe(topic, message => {
observer.next(handler(message));
});
return () => {
console.log("Unsubscribe from socket-client service");
client.unsubscribe(subscription .id);
}
});
}));
}
In order to create an Observable, you can use new Observable or a creation operator. See the following example:
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
});
You can provide a function unsubscribe() to allow dispose of resources, and that function goes inside subscribe() as follows:
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
return function unsubscribe() {
console.log('Clearing resources on observable');
};
});
Of course, you can use an arrow function expression to have:
const observable = new Observable((observer) => {
observer.next(1);
observer.next(2);
observer.next(3);
return () => {
console.log('Clearing resources on observable');
};
});
Try the following code to test the Observable:
const subscription = observable.subscribe(res => console.log('observable data:', res));
subscription.unsubscribe();
Finally, subscription.unsubscribe() is going to remove the socket connection in your example.
Find a project running with these examples here: https://stackblitz.com/edit/typescript-observable-unsubscribe
Let me know if that helps!

A sinonjs stub in the "then"

I have a class A , and I want to test the eventHandler method.
class A () {
eventHandler (controller) {
controller.exec().then((() => {
this._afterHandler(); // I can't stub it.
}))
// this._afterHandler(); // I can stub it !!!
}
_afterHandler() {
xxxxx...;
}
}
This is my test code . I find that I can not stub the _afterHandler method. when the method in the then. but when I move the method to the "then" outside. I can stub it.
it('xxxx', () => {
const a = new A();
const stub = sinon.stub(a,'_afterHandler');
a.eventHandler({ exec: () => {return Promise.resolve(1)} })
sinon.assert.calledOnce(stub);
});
How can I stub the _afterHandler method ?? Thanks ~~
_afterHandler is getting stubbed by Sinon.
The problem here is that sinon.assert.calledOnce(stub) is getting called before the stubbed _afterHandler - thanks to the use of promises.
In terms of making this testable, one option is like:
class A {
eventHandler (controller) {
return controller.exec().then(() => {
this._afterHandler();
})
}
_afterHandler() {
xxxxx...;
}
}
and the test:
it('xxxx', async () => {
const a = new A();
const stub = sinon.stub(a,'_afterHandler');
await a.eventHandler({ exec: () => {return Promise.resolve(1)} });
sinon.assert.calledOnce(stub);
});

Event Emitter Javascript

Hi i am a newbie in javascript. I am implementing emitter on function in javascript and i have trouble in testing it using jasmine framework. Below is the function emitter on and code to test the function.
//main.js
Emitter.prototype.on = function (event, listener)
{
if (typeof this.events[event] !== 'object')
{
this.events[event] = [];
}
this.events[event].push(listener);
};
//test.js
describe('#on', () =>
{
it('should subscribe to an event name and be called when
triggered', () =>
{
Emitter.on(EVENT_NAME_ONE, spyFunction);
//Emitter.trigger(EVENT_NAME_ONE);
expect(spyFunction.calls.count()).toBe(1);
});
The above mentioned test fails. I am not sure why. Can someone please help me with this. Thanks.
The error says Emitter.on is undefined. See this thread.
on is defined on Emitter instance.
class Emitter {
constructor() {
console.log('created Emitter!')
}
on(event, callback) {
console.log('on')
}
}
a = new Emitter()
=> created Emitter!
a.on()
=> on
I could solve that error. i had to export the function to the tests and changed the browser to chrome in karma.conf.js file . Now i get this TypeError: Cannot read property 'event_name_one' of undefined. I have it defined
var Emitter = function () {
this.events = {};
var Event_Name_One;
} ;
export function on (event, listener) {
if (typeof this.events[event] !== 'object') {
this.events[event] = [];
}
this.events[event].push(listener);
};
and in test.js
// Testing event names
const EVENT_NAME_ONE = 'event_name_one';
describe('#on', () => {
it('should subscribe to an event name and be called when triggered', () => {
Emitter.on(EVENT_NAME_ONE, spyFunction);
Emitter.trigger(EVENT_NAME_ONE);
expect(spyFunction.calls.count()).toBe(1);
});
I don't understand why i get this error. Can anyone provide help regarding this.thanks.

Categories

Resources