How to get `this` in jest mock function - jestjs

I have the below code, which mocks a function fetchAge of Person. I need to conditionally return a value based on the value of this, but this is an empty object in the function. Is there any way to do this in Jest?
jest.unmock('Person');
const Person = require('Person');
Query.prototype.fetchAge = jest.fn(() => {
console.log(this); // this returns an empty object
if(this.name === 'Bob') return 21;
if(this.name === 'Joe') return 19;
});
test('verify correct ages', function() {
const bob = new Person('Bob');
expect(bob.fetchAge).toEqual(21);
});

If you don't need to assert anything on the mocked fetchAge method (like fetchAge.mock.calls.length for example) you could simply assign the fetchAge method to a new function without using jest.fn.
E.g.
Query.prototype.fetchAge = () => {
console.log(this); // this returns an empty object
if(this.name === 'Bob') return 21;
if(this.name === 'Joe') return 19;
};
I think that way this will relate to the correct object (here bob).

Related

Mock function without callback as parameter

I have dh.js
const checkDExistsCallback = (err, dResp) => {
if (err)
cbResp.error('failed');
if (dResp.length > 0)
checkDCollectionExists();
else
cbResp.error('Not found.');
};
const checkDCollectionExists = () =>
{
let query = `select sid from tablename where sid = '${objRequestData.dName}' limit 1;`;
genericQueryCall(query, checkDCollCallback);
}
module.exports = {checkDExistsCallback , checkDCollectionExists }
In my dh.test.ts
const dhExport = require("./DensityHookReceive");
dhExport.checkDCollectionExists = jest.fn().mockImplementation(() => {});
test('check req dh is exists', () => {
dhExport.checkDExistsCallback(false, '[{}]');
expect(dhExport.checkDCollectionExists).toBeCalled();
});
In dh.js checkDExistsCallback function is invoked the checkDCollectionExists after satisfied the 'if' condition. When you look into the dh.test.ts file I mocked the checkDCollectionExists function in the beginning, but while running the test it did not invoke the mocked function it invokes the actual function. Can you help me to figure it out?
A function that is used in the same module it was defined cannot be mocked, unless it's consistently used as a method on an object that could be mocked, e.g.
if (dResp.length > 0)
module.exports.checkDCollectionExists();
instead of
if (dResp.length > 0)
checkDCollectionExists();
checkDCollectionExists needs to be either moved to another module, or two functions need to be tested as a single unit. It's database call that needs to be mocked.

Calling a method within a dynamically named object

I am trying to invoke a method of a dynamically named object.
I have a few objects each containing a method named 'getWeight'. I need to invoke this method of a subset of these objects. However, the subset of objects depends on user inputted info, which is why I'm attempting to dynamically construct the object name and invoke it's 'getWeight' method within a loop.
My code is below:
// Importing objects containing 'getWeight' method
const Apple = require('../classses/Apple');
const Pear = require('../classes/Pear);
const Carrot = require('../classes/Carrot);
const Potato = require('../classes/Potato);
const promises = {};
const user = 'userX'; // This is the inputted info, could also equal 'userY'.
const searchableFoods = {
userX: ['Apple', 'Pear'],
userY: ['Carrot', 'Potato']
};
for (i = 0; i < searchableFoods[user].length; ++i) {
promises[searchableFoods[user][i]] = new Promise(function(resolve, reject) {
// Below line should behave like: Apple.getWeight(arg1, function....
searchableFoods[user][i].getWeight(arg1, function(response) {
resolve(response);
})
})
}
Unfortunately, I get this error:
[searchableFoods[user][i].getweight] is not a function
I've tried a number of variations but can't make it work. Is it possible to do this?
Require all of those into a single object rather than many standalone variables, and then you can use simple property lookup to get to the appropriate requireed value. Also, forEach will likely result in more readable code, and you can pass the function name alone (resolve) rather than defining an extra anonymous function for the getWeight callback:
const foods = {
Apple: require('../classses/Apple'),
Pear: require('../classes/Pear),
Carrot: require('../classes/Carrot),
Potato: require('../classes/Potato)
};
// ...
searchableFoods[user].forEach((foodName) => {
promises[foodName] = new Promise((resolve) => {
foods[foodName].getWeight(arg1, resolve);
});
});
Can you define array not using strings? Something like that?
const searchableFoods = {
userX: [Apple, Pear],
userY: [Carrot, Potato]
};
You're trying to access getWeight as a property of string 'Apple' and not of the actual object Apple that you are importing.
Change the subset array to something like this
const searchableFoods = {
userX: [Apple, Pear],
userY: [Carrot, Potato]
};
Which makes the final code to be
const Apple = require('../classses/Apple');
const Pear = require('../classes/Pear);
const Carrot = require('../classes/Carrot);
const Potato = require('../classes/Potato);
const promises = {};
const user = 'userX' // This is the inputted info, could also equal 'userY'.
const searchableFoods = {
userX: [Apple, Pear],
userY: [Carrot, Potato]
};
for (i = 0; i < searchableFoods[user].length; ++i) {
promises[searchableFoods[user][i]] = new Promise(function(resolve, reject) {
// Below line should behave like: Apple.getWeight(arg1, function....
searchableFoods[user][i].getWeight(arg1, function(response) {
resolve(response);
})
})
}

Nodejs async function prototype chain error

why this code compiles
var Person = function() {
console.log("CALLED PERSON")};
Person.prototype.saySomething = function() {
console.log("saySomething PERSON")};
var ape = new Person();
ape.saySomething();
and this code throws error Cannot set property 'saySomething' of undefined
var Person = async function() {
console.log("CALLED PERSON")};
Person.prototype.saySomething = function() {
console.log("saySomething PERSON")};
var ape = new Person();
ape.saySomething();
When you use async function() {}, you are declaring an asynchronous function object. That's different than a regular function object. The asynchronous function object does not have a prototype.
So, when you try to do this:
var Person = async function() {
console.log("CALLED PERSON")
};
Person.prototype.saySomething = function() {
console.log("saySomething PERSON")
};
Person.prototype is undefined because there is no prototype on an asynchronous function object. Thus attempting to assign something to Person.prototype.saySomething causes the error you see because Person.prototype is undefined.
There is some logic to this because an asynchronous function can't be used as a constructor because an asynchronous function always returns a promise so it can't ever return a new object as in let obj = new f(). So, there's no purpose in having a .prototype property because it can't be used that way.
If you really wanted to asynchronously create an object, you could always create an async factory function that returns a promise that resolves with an object.
It is possible to add an async function in the end of the prototype chain.
Please notice that this works well in nodejs 8.11.1+.
// lets start with defining some cool async function who takes the time and
// awaits a promise
async function someCoolAsyncFunction() {
// this let will be returned after timeout in a different value.
let coolStuff = 'still boring';
// do something cool which takes time
await new Promise((resolve, reject) => setTimeout(() => {
coolStuff = 'an epiphany';
resolve();
}, 1000))
return coolStuff;
}
// Now let's define the 'regular' prototype chain with it's boring functions.
function Person(p) {
this.constructorPropery = p;
return this;
}
Person.prototype.notAsync = function() {
// do something regular in the prototype chain
console.log("Let's build some ",this.constructorPropery)
this.kindOfPerson = 'Regular and boring';
return this;
}
// And now, lets add an async function to this chain
Person.prototype.someAsyncFunction = async function() {
// you will still have access to 'this' in this function as long as the
// previous function in the prototype chain returnes 'this'
console.log('I used to be someone ',this.kindOfPerson);
// Now, this is our time to shine, lets await something cool
this.lifeChangingEvent = await someCoolAsyncFunction();
console.log('Until I had ',this.lifeChangingEvent);
a.kindOfPerson = 'enlightened';
console.log('and now I am ', a.kindOfPerson);
return this;
}
So this will work:
new Person('charachter').notAsync().someAsyncFunction();
But this WILL NOT work:
new Person('charachter').someAsyncFunction().notAsync();
And if you really need the data in 'this' outside the prototype chain you can also do:
let myself = new Person('charachter').notAsync();
console.log('myself.kindOfPerson is: ',myself.kindOfPerson);
myself.someAsyncFunction();
console.log('myself.kindOfPerson now is: ',myself.kindOfPerson);
Be sure to remember which prototype is an async function for which function or use your own naming convection for that.

Get the value of a promise and assign to variable

utility.fetchInfo() returns a Promise object. I need to be able to get the value of this Promise object and assign the value of it to a variable that I can then use later on in my code.
At the moment, I can happily print the value of result to the console, but I need to be able to assign this value to myVal. So far, I've tried a lot of things and nothing has worked.
var myVal = utility.fetchInfo().then(result => console.log(result));
Thanks
What #dhilt said but do your stuff inside the promise callback function:
utility.fetchInfo().then(result => {
doSomethingWith(result);
});
Or use async/await if you are using a recent version of Node or Babel:
async function myFunction () {
var myVal = await utility.fetchInfo();
}
Just do an assignment within the callback's body
utility.fetchInfo().then(result => { myVal = result; });
Depends on the situation, for example, if there's a big piece of async logic, it may be better to extract it in a separate function:
let myVal; // undefined until myAsyncFunc is called
const myAsyncFunc = (result) => {
console.log(result);
myVal = result;
// ...
};
utility.fetchInfo().then(myAsyncFunc); // async call of myAsyncFunc
When you return the value to another file/function and you are getting undefined because another function where you are using that value is not waiting for the value to be assigned to the variable. For example there are two function
function1(){
var x=desired_value
}
function2(){
var y = x
}
now function1 might take some time to assign desire value to var x and java script ll not wait for this, it ll start running function2 and when you read the value of y you will get undefined.
To solve this problem we can use java script promise/await/async, return value from async function as resolve. refer below example
async function1(){ //function1 should be async here for this to work
return new Promise(function(resolve,reject){
resolve(desired_value)
});
}
or above function can be writtern as
async function1(){ //function1 should be async here for this to work
return Promise.resolve(desired_value)
}
Now we can use this value in another function by calling the function1 and use await keyword
function2(){
var y = await function1();
}
now it ll wait for function1 to complete before assigning its value to y.
I think what you are looking for is some fancy ES7 syntax:
var myVal = (async () => {
var data = await utility.fetchInfo();
return data;
})();
(async () => {
console.log(await myVal);
})();
Keep in mind console.log(myVal) will end up with Promise { <pending> }, So instead you would use
(async () => {
console.log(await myVal);
})();
which would return your desired output.

Sinon not mocking method

I want inject into constructor database client, but when I run tests, mocha throw exception, that method whitch is called is not a function.
export class CustomService {
constructor(database: any) {
database.init().then((res)=>{}));
}
}
describe('CRUD service', ()=>{
it('when i decide save item', ()=>{
let db = sinon.mock(new DatabaseService);
let instance = new CustomService(db);
db.expects('init').once();
db.verify();
});
});
In console:
TypeError: database.init is not a function
What is wrong?
Don't pass the return value of sinon.mock to the code you are testing but instead pass the original object you passed to sinon.mock. The return value of sinon.mock is only for setting assertions and checking them. You also need to set the order of the statements in your tests so that the expectations are set before the code that must satisfy them is run. Something like this:
describe('CRUD service', ()=>{
it('when i decide save item', ()=>{
const db = new DatabaseService();
let mock = sinon.mock(db);
mock.expects('init').once();
let instance = new CustomService(db);
mock.verify();
});
});

Resources