Error in testing a Meteor helper function with Jasmine - javascript

I have a helper function in MeteorJS as given below:
Template.cars.helpers({
models : function() {
var currentUserId = Meteor.userId();
return cars.find({userId: currentUserId});
}
});
I am trying to write a Jasmine test which will check if the Meteor.userId() function is called once when the models helper is called. I have mocked the Meteor.userId() function,and my code is given below:
describe("test cars collection", function() {
beforeEach(function() {
var Meteor = {
userId: function() {
return 1;
}
};
});
it("userId should be called once", function() {
Template.cars.__helpers[' models']();
spyOn(Meteor, 'userId').and.callThrough();
expect(Meteor.userId.calls.count()).toBe(1);
});
});
However, the result is showing Expected 0 to be 1. I am new to Jasmine and do not really know how to make the correct call to the Meteor.userId() function, while calling the models helper. I think the way I am spying on it is wrong, but I have not been able to figure it out. Can somebody help please?

From the Jasmine Docs
.calls.count(): returns the number of times the spy was called
But you need to set the spy before you call the function, so you can check if your function has been called only once like so:
it("userId should be called once", function() {
spyOn(Meteor, 'userId').and.callThrough(); // <-- spy on the function call first
Template.cars.__helpers[' models'](); // <-- execute the code which calls `Meteor.userId()`
expect(Meteor.userId.calls.count()).toBe(1); // <-- Now this should work
});

Related

Stubbing and/or spying on an optional global function: Sinon, mocha & chai

I have a method that checks whether or not a global function is defined (it may or may not be available, depends on each client's request). If it is defined, it will call it with the appropriate data. If not, it will fail silently. That's the desired behavior.
What I want to do is test it. Is there a way to mock and/or spy on libFunction so that I can ensure it's being called once with the correct data (the function here is very much simplified, there's some data processing that happens along the way).
Here's the method in question:
function sendData(data) {
let exists;
try {
// eslint-disable-next-line no-undef
if (libFunction) exists = true;
} catch (e) {
exists = false;
}
if (exists) {
// eslint-disable-next-line no-undef
libFunction(data);
}
}
I've tried defining libFunction in my tests and then stubbing that, but that doesn't do what I want:
describe('sendEvent', function () {
function libFunction(data) {
console.log('hi', data);
}
it('should call libFunction once', function () {
var stub = sinon.stub(libFunction);
var data = "testing";
sendEvent(data);
expect(stub.called).to.be.true;
});
});
This test does not pass, however: AssertionError: expected undefined to be true
I've tried something similar with a spy:
describe('sendEvent', function () {
function libFunction(data) {
console.log('hi', data);
}
it('should call libFunction once', function () {
var spy = sinon.spy(libFunction);
var data = "testing";
sendEvent(data);
expect(spy.called).to.be.true;
});
});
This also fails: AssertionError: expected false to be true
Is there a way to do this?
FWIW, I came across this question while trying to solve an issue for stubbing a global method in Node. In my case, this worked (my example uses Sinon.sandbox, but "regular" Sinon.spy should work, as well):
const encodeSpy = sandbox.spy(global, "encodeURIComponent");
// later...
Sinon.assert.calledWith(encodeSpy, {expectedParamValue});

Testing JavaScript callback functions with jasmine

I have the following functions:
function getPersonData(id) {
retrieveData(
id,
function(person) {
if(person.name) {
displayPerson(person);
}
}
}
function retrieveData(id, successCallBack) {
executeRequest(id, {
success: successCallBack
});
}
getPersonData retrieves a person's information based on the id. It in turn calls retrieveData by passing in the id and a successCallBack function.
retrieveData takes the id and successCallBack and calls another function, executeRequest, which gets the data and passes back a person object.
I am trying to test getPersonData and have the following spec set up
describe("when getPersonData is called with the right person id", function() {
beforeEach(function() {
spyOn(projA.data, "retrieveData").and.returnValue(
{
'name': 'john'
}
);
spyOn(projA.data, "displayPerson");
projA.data.getPersonData("123");
});
it("displays the person's details", function() {
expect(projA.data.displayPerson).toHaveBeenCalled();
);
});
But when the spec is executed the displayPerson method isn't called. This is because the person data being passed back from the success callBack function(person) isn't being passed in even though I have mocked retrieveData to return a result.
My question is:
Is this the right way to test callBack functions? Either way what am I doing wrong?
Ok, so jasmine is tricky in a lot of subtle ways and I think there's two main issues with your code
You have way too many asynchronous calls wrapped in each other. Which is by itself not a problem, but it makes testing in JASMINE hell of a lot harder. For example, what is the point of having a retrieveData function which just calls executeRequest function with the exact same parameters but in a slightly different way.
I rewrote your getPersonData to be like this
function getPersonData(id) {
// this is needed to properly spy in Jasmine
var self = this;
//replaced retrieveData with just execute request
// self is required to properly spy in Jasmine
self.executeRequest(id, {
success: function(person) {
if (person.name) {
self.displayPerson(person);
}
}
})
}
//I don't know what exactly executeRequest does
//but I took the liberty to just make it up for this example
function executeRequest(id, callback) {
callback.success({
name: id
});
}
//I also assumed that projA would look something like this
var projA = {
data: {
getPersonData: getPersonData,
retrieveData: retrieveData,
displayPerson: displayPerson,
executeRequest: executeRequest
}
};
2. In order to test asynchronous code in Jasmine, you need to include a done callback with the test. Also, if you expect a callback function to fire automatically, you need to set it up within a setTimeout function, otherwise it will never fire. Here's an adjusted example:
describe("when getPersonData is called with the right person id", function() {
beforeEach(function() {
//you should not spyOn retriveData or executeCode because it will override the function you wrote and will never call displayPerson
// you should only have spies on the methods you are testing otherwise they will override other methods
spyOn(projA.data, "displayPerson");
});
it("displays the person's details", function(done) {
// it's better to call the function within specific test blocks so you have control over the test
projA.data.getPersonData("123");
// at this point, it will just have called the getPersonData but will not call executeRequest
setTimeout(function() {
//this block will just call executeRequest
setTimeout(function() {
//this block will finally call displayPerson
expect(projA.data.displayPerson).toHaveBeenCalled();
//tell jasmine the test is done after this
done();
})
})
});
})

Jasmine testing methods inside .done or .then

Wondering if anyone can help me - I'm trying to test my js using Jasmine (1.3) and I can't figure out the best way to test any method calls inside a .then or a .done method.
example code to explain:
Backbone.View.extend({
myMethod: function () {
this.something.done(function () {
this.doSomethingElse();
}.bind(this));
}
})
I'd like to write a test that check that this.doSomethingElse was called.
I was looking around at jasmine.async and a waitsFor/runs set up but I'm not sure how it fits into external code i.e. I'm not going to call done() inside my actual code to get my test working. Also if I mock out the done method on this.something then I'm not longer testing the actual implementation, right?
I'm just missing how things fit together. If anyone could point me in the right direction I'd really appreciate it!
Update: based on feedback below I've now tried the following
Hey, thanks for the answer - I think maybe I don't have the last part correct - have tried 2 different ways, both initial pass but then fail after a second or 2.
it('calls doSomethingElse on done',function () {
var mockDeferred = $.Deferred();
myView.something = mockDeferred;
spyOn(myView,'doSomethingElse');
mockDeferred.resolve();
waitsFor(function () {
expect(myView.doSomethingElse).toHaveBeenCalled();
});
});
And also:
it('calls doSomethingElse on done',function () {
var mockDeferred = $.Deferred(),
someTrigger = false;
myView.something = mockDeferred;
spyOn(myView,'doSomethingElse');
runs(function () {
mockDeferred.resolve();
someTrigger = true;
});
waitsFor(function () {
someTrigger = true;
});
runs(function () {
expect(myView.doSomethingElse).toHaveBeenCalled();
});
});
In both instances the test will pass originally but then timeout to a failure after a second or 2.
Am I missing something?
To test the example function you described, I would do the following within your test:
Create a new deferred object (I'll call it mockDeferred)
Pass mockDeferred into your code under test so that it is now this.something in your example
Spy on the doSomethingElse function
Call myMethod()
Call resolve() on mockDeferred
Assert that doSomethingElse was called
Edit based on OP's Update:
I don't see anywhere in either of your examples where you are calling myView.myMethod() within your test; make sure you do that. I whipped up an example that you can reference here.
As an aside, I'm surprised the second example you tried passes initially. Maybe because you have some code outside of a runs() block?
Related problem
Spying on a method inside .then and expecting .toHaveBeenCalled fails
Solution:
run test inside fakeAsync and run tick() before the expect
Service:
getFirebaseDoc() {
this.db.firestore.doc('some-doc').get()
.then(this.getFirebaseDocThen)
.catch(this.getFirebaseDocCatch);
}
Unit testing:
it('should call getFirebaseDocThen', fakeAsync(() => { // note `fakeAsync`
spyOn(service, 'getFirebaseDocThen');
spyOn(service.db.firestore, 'doc').and.returnValue({
get: (): any => {
return new Promise((resolve: any, reject: any): any => {
return resolve({ exists: true });
});
},
});
service.getFirebaseDoc();
tick(); // note `tick()`
expect(service.getFirebaseDocThen).toHaveBeenCalled();
}));

sinon spy not wrapping method in asynchronous callback

It appears that sinon.spy(object, method) is not wrapping my object#method as expected.
(I have an uneasy feeling that I'm seeing the same problem as described here and here, but I don't see why this should be. I've instantiated my object before calling sinon.spy(...), and AFAIK I'm not using any cached objects.)
Here's the complete test file:
var
AmbitParser = require('../lib/parsers/ambit-parser'),
expect = require('chai').expect,
sinon = require('sinon');
describe('AmbitParser', function() {
var ambit_parser = new AmbitParser();
describe('#extractLineItems()', function() {
it('calls extractLineItems once', function(done) {
var spy = sinon.spy(ambit_parser, 'extractLineItems');
ambit_parser.parseBills(function gotBills(err, bills) {
expect(ambit_parser.extractLineItems.callCount).to.equal(1); // => expected undefined to equal 1
expect(spy.callCount).to.equal(1); // => expected 0 to equal 1
done();
});
ambit_parser.extractLineItems.restore();
}); // calls extractLineItems once
}); // #extractLineItems
}); // AmbitParser
The call to expect(ambit_parser.extractLineItems.callCount).to.equal(1); results in 'expected undefined to equal 1' and if I change that to expect(spy.callCount).to.equal(1);, I get 'expected 0 to equal 1'.
Together, this makes me think that the call to sinon.spy(...) is not wrapping the ambit_parser.extractLineItems method as expected, but I can't see why this is the case.
The problem is the placement of the call to restore(): it should not be in the body of the test function. Instead, put it in an after() block.
What's happening is that the restore() method is getting called immediately after the test is started, so by the time the callback is executed, the spied upon method has been restored, so sinon will report that the method has never been called.
The following modifications to the original code will work as expected:
describe('AmbitParser', function() {
var ambit_parser = new AmbitParser();
describe('#extractLineItems()', function() {
before(function() {
sinon.spy(ambit_parser, 'extractLineItems');
});
after(function() {
ambit_parser.extractLineItems.restore();
});
it('calls extractLineItems once', function(done) {
ambit_parser.parseBills(function gotBills(err, bills) {
expect(ambit_parser.extractLineItems.callCount).to.equal(1);
done();
});
}); // calls extractLineItems once
}); // #extractLineItems
}); // AmbitParser
Moral of the story: make sure you call reset() only after any callbacks have completed.

Jasmin spyOn only works when spied-on function is wrapped, but not when delegated to

In this contrived case, I have a service that takes one dependency. The service exposes one method that needs to call through to the dependency.
If I wrap this method call in its own function, then spyOn works as expected (MyService1). However, if I delegate to this service directly, then spyOn fails (MyService2).
My questions are:
Can someone please explain this behaviour?
If I want to use spyOn, must I re-write all my directly delegated methods as wrappers, or is there a way around this?
Thanks.
describe('spyOn problem', function() {
// wraps dependency function - can spy on
var MyService1 = function (dependency) {
this.continue = function() {
dependency.nextStage();
};
};
// delegates to dependecy function - cannot spy on
var MyService2 = function (dependency) {
this.continue = dependency.nextStage;
};
var dependencyMock = {
nextStage: function () {
}
};
it('should call nextStage method of MyService dependency', function () {
var service = new MyService1(dependencyMock);
spyOn(dependencyMock, 'nextStage'); // also tried with .andCallThrough()
service.continue();
expect(dependencyMock.nextStage).toHaveBeenCalled();
// Fail: "Expected spy nextStage to have been called"
});
it('should call nextStage method of MyService2 dependency', function () {
var service = new MyService2(dependencyMock);
spyOn(dependencyMock, 'nextStage');
service.continue();
expect(dependencyMock.nextStage).toHaveBeenCalled();
// Pass
});
});
Jasmine Spies works on the objects, which means for spyOn(dependencyMock, 'nextStage'), the function nextStage on the object dependencyMock will be replaced with a jasmine spy function with this statement.
In MyService2 test case, the spy is installed after actually assigning the function nextStage to continue, which means continue will refer to the actual function nextStage and not to spy. By modifying the test case as below, the spy will be assigned instead.
it('should call nextStage method of MyService2 dependency', function () {
spyOn(dependencyMock, 'nextStage');
var service = new MyService2(dependencyMock);
service.continue();
expect(dependencyMock.nextStage).toHaveBeenCalled();
// Pass
});
In the MyService1 test case, even though you are calling service.continue(), it internally operates on 'dependencyMask` object for which spy is installed.

Categories

Resources