Jest spying on function called from within another - jestjs

I have the following two files:
functions.js
function getOne() {
return 1;
}
function getTen() {
let val = 0;
for (let x = 0; x < 10; x++) {
val+= getOne();
}
return val;
}
module.exports = {
getOne,
getTen,
}
functions.test.js
const numberFunctions = require('../functions.js');
const getOne = numberFunctions.getOne;
const getTen = numberFunctions.getTen;
// passes
test('I should be able to get the number 1', () => {
expect(getOne()).toBe(1);
});
describe('The getTen function', () => {
// passes
it('should return 10', () => {
expect(getTen()).toBe(10);
});
// fails
it('should call the getOne method 10 times', () => {
const spy = jest.spyOn(numberFunctions, 'getOne');
expect(spy).toHaveBeenCalledTimes(10);
});
});
I'm trying to ensure that the the function getOne has been called from within the getTen function. In total it should be called 10 times, but my spy always claims that it has been called zero times.
I've tried rearranging my test so that I mock the getOne function as a global e.g.
it('should call the getOne method 10 times', () => {
global.getOne = jest.fn(() => 1);
expect(global.getOne).toHaveBeenCalledTimes(10);
});
But this results in the same outcome. How can I spy on the getOne function being called from within the getTen function?

I managed to get this working by changing the getOne function called by getTen to directly reference the one exported by the function. Otherwise it seems to be referencing some internally scoped function which is not the one that is exported, making it impossible to spy on.
This is explained in more detail in this github conversation, but to get my test to work as expected meant that I had to refactor my code to be:
function getOne() {
return 1;
}
function getTen() {
let val = 0;
for (let x = 0; x < 10; x++) {
val+= module.exports.getOne(); // <-- this line changed
}
return val;
}
module.exports = {
getOne,
getTen,
}
Now, rather than being internally scoped, the inner function refers to the exported one and can be spied on.

Related

Call a async function every minute for 5 minutes in nodejs

I am trying to call a async function every minute for 5 minutes before exiting the main function. Below is the print_data() function which I am calling in main() function.
var print_data = async () => {
console.log("Hello")
}
async function main() {
process.stderr.write("--Start--")
var data = await print_data()
console.log(data)
}
main()
Very new to writing async function. What is the best way to call print_data function every minute for 5 minutes and print the output each minute? I tried something using setInterval and was not able to execute the function completely.
Any help would be good. Thank you in advance.
This is one way to do it using setInterval and clearInterval. Read more about it here: https://nodejs.org/api/timers.html#timers_clearinterval_timeout
Using IIFE to prevent polluting of the global scope.
(function (){
let counter = 0; //counter to keep track of number of times the setInterval Cb is called
let data; // to store reference of Timeout object as returned by setInterval, this is used in clearInterval
const print_data = async () => {
console.log("Hello")
counter++;
if (counter == '5') {
clearInterval(data);
}
}
async function main() {
process.stderr.write("--Start--")
data = setInterval(print_data, 1000*60); //60 seconds
}
main();
})();
Please check if the below code can be a solution.
var print_data = async () => {
console.log("Hello")
return "Hello";
}
var call_print_data = () => new Promise((resolve, reject) => {
var count = 0;
var interval = setInterval(async () => {
var res = await print_data();
count += 1;
if (count === 5) { // if it has been run 5 times, we resolve the promise
clearInterval(interval);
resolve(res); // result of promise
}
}, 1000 * 60); // 1 min interval
});
async function main() {
process.stderr.write("--Start--")
var data = await call_print_data(); // The main function will wait 5 minutes here
console.log(data)
}
main()

NodeJS constructing array from asynchronious callbacks before returning

I'm writing a function that's returning and array of values. Some of the values are calculated in a callback. But I don't know how to make the program asynchronious so all of my results are in the array, and not added after they're returned.
let array = []
for (stuff : stuffs) {
if (condition) {
array.add(stuff)
} else {
api.compute(stuff, callback(resp) {
array.add(resp.stuff)
}
}
}
res.json({ "stuff": array })
In this example the array is written to the response before the async calls have finished.
How can I make this work asynchronously?
You have to use one of the approaches:
async library
Promise.all
coroutines/generators
async/await
The most cool yet, I think, is async/await. First we modify your function, so it returns a promise:
const compute = function(stuff) {
return new Promise( (resolve, reject) => {
api.compute(stuff, callback(resp){
resolve(resp.stuff)
});
});
};
Then we modify your route with async handler:
app.get('/', async function(req, res, next) {
const array = [];
for (const stuff of stuffs) {
if (condition) {
array.add(stuff);
} else {
const stuff = await compute(stuff);
array.push(stuff);
}
}
res.json({ stuff: array });
});
Note: You might need to update node version to latest.
UPDATE:
Those who are not awared, how event loop works, execute this snippet, and finish with that:
const sleep = async function(ms) {
console.log(`Sleeping ${ms}ms`);
return new Promise( resolve => setTimeout(resolve, ms));
};
async function job() {
console.log('start');
for (let t = 0; t < 10; t++) {
await sleep(100);
}
}
job();
console.log('oops did not expect that oO');
You will be surprised.
Here is an answer without package using callbacks
Create a function that's gonna recursively treat all your stuffs.
getArray(stuffs, callback, index = 0, array = []) {
// Did we treat all stuffs?
if (stuffs.length >= index) {
return callback(array);
}
// Treat one stuff
if (condition) {
array.add(stuffs[index]);
// Call next
return getArray(stuffs, callback, index + 1, array);
}
// Get a stuff asynchronously
return api.compute(stuffs[index], (resp) => {
array.add(resp.stuff);
// Call next
return getArray(stuffs, callback, index + 1, array);
});
}
How to call it?
getArray(stuffs, (array) => {
// Here you have your array
// ...
});
EDIT: more explanation
What we want to do to transform the loop you had into a loop that handle asynchronous function call.
The purpose is that one getArray call gonna treat one index of your stuffs array.
After treating one index, the function will call itself again to treat the next index, until all get treated.
-> Treat index 0 -> Treat index 1 -> Treat index 2 -> Return all result
We are using parameters to pass the infos through the process. Index to know which array part we have to treat, and array to keep a tract of what we did calculate.
EDIT: Improvement to 100% asynchronous soluce
What we have done here it's a simple transposition of your initial for loop into an asynchronous code. it can be improved so by making it totally asynchronous, which make it better but slightly more difficult.
For example :
// Where we store the results
const array = [];
const calculationIsDone = (array) => {
// Here our calculation is done
// ---
};
// Function that's gonna aggregate the results coming asynchronously
// When we did gather all results, we call a function
const gatherCalculResult = (newResult) => {
array.push(newResult);
if (array.length === stuffs.length) {
callback(array);
}
};
// Function that makes the calculation for one stuff
const makeCalculation = (oneStuff) => {
if (condition) {
return gatherCalculResult(oneStuff);
}
// Get a stuff asynchronously
return api.compute(oneStuff, (resp) => {
gatherCalculResult(resp.stuff);
});
};
// We trigger all calculation
stuffs.forEach(x => x.makeCalculation(x));

How to mock function using jest.fn()

The jest document says, There are two ways to get your hands on mock functions: Either by require()ing a mocked component (via jest.mock('moduleName')) or by explicitly requesting one from jest.fn() in your test:
So I am trying mocking a function thinking whenever a test makes a call to the minus function, it executes the mocked function and returns 10 instead of a*b but it's not working the way I thought.
app.js:
function add(a, b) {
return multiply(a, b) + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = add;
add-test.js
const add = require('./add');
describe('add', () => {
it('should add two numbers', () => {
const multiply = jest.fn((a, b) => {10})
expect(add(1, 2)).toBe(12);
});
});
Note: If I change expect(add(1, 2)).toBe(12); to expect(add(1, 2)).toBe(4); the test will pass, but I want to mock multiply and return 10. So when I actually call made to multiply function then it should invoke mocked function and not real implementation.
You will have to change the structure of app.js slightly to test this the way you want.
You can either extract multiply() into its own file like this:
mulitiply.js
function multiply(a, b) {
return a * b;
}
app.js
function add(a, b) {
return multiply(a, b) + b;
}
add.test.js
const add = require('./add');
const multiply = require('./multiply');
jest.mock('multiply', () => jest.fn(a, b) => 10)
describe('add', () => {
it('adds the numbers', () => {
expect(add(1, 2)).toBe(12);
});
});
or change the implementation to be something like this:
add.js
function add(multiply) {
multiply()
}
add.test.js
const add = require('./add');
describe('add', () => {
it('adds the numbers', () => {
const multiply = jest.fn(a,b) => 10;
expect(add(multiply(1, 2)).toBe(12);
});
});

this.function is undefined for .prototyped arrow function [duplicate]

This question already has answers here:
Are 'Arrow Functions' and 'Functions' equivalent / interchangeable?
(4 answers)
Closed 6 years ago.
This code:
'use strict'
function Ob() {}
Ob.prototype.add = () => {
this.inc()
}
Ob.prototype.inc = () => {
console.log(' Inc called ');
}
module.exports = new Ob();
Is used by this code:
'use strict'
const ob = require('./ob')
ob.add();
When calling the ladder one, I get this error:
this.inc is not a function
When I change the first snippet to this:
'use strict'
function Ob() {}
Ob.prototype.add = function() {
this.inc();
}
Ob.prototype.inc = function() {
console.log(' Inc called ');
}
module.exports = new Ob();
Everything is fine and I get:
Inc called
Why does the first version throw?
Update: How would I make it work using arrow functions?
It doesn't work using arrow functions because they capture the this of the current scope. In your sample code
'use strict'
function Ob() {}
Ob.prototype.add = () => {
this.inc()
}
Ob.prototype.inc = () => {
console.log(' Inc called ');
}
module.exports = new Ob();
there is no this when this code runs (okay, there is one, but at least it's not the object instance that was created by the constructor). Essentially, this code is equivalent to:
'use strict'
const that = this;
function Ob() {}
Ob.prototype.add = function () {
that.inc()
}
Ob.prototype.inc = function () {
console.log(' Inc called ');
}
module.exports = new Ob();
and then it becomes apparent that actually there is no function for that.inc(). That's why it does not work.
How to fix this using arrow functions? Well, I'd say, don't use arrow functions here, because it makes absolutely no sense, and they are not just another way of writing "normal" functions, they are something slightly different.
How to fix this in general? Use the old-fashioned function keyword, and everything will work as expected :-)
Arrow functions don't introduce a new context. What that means is within an arrow function this will be the context from the function that it's contained within.
In the following example, the IIFE creates a context of window
(function () {
function Ob() {}
Ob.prototype.test = () => {
console.log(this === window);
};
o = new Ob();
o.test(); // true
}());
If you want to use a function with its own context, you'll have to use the full function keyword.
(function () {
function Ob() {}
Ob.prototype.test = function () {
console.log(this === window);
};
o = new Ob();
o.test(); // false
}());

Asynchronous nodejs module exports

I was wondering what the best approach is for configuring a module export. "async.function" in the example below could be a FS or HTTP request, simplified for the sake of the example:
Here's example code (asynmodule.js):
var foo = "bar"
async.function(function(response) {
foo = "foobar";
// module.exports = foo; // having the export here breaks the app: foo is always undefined.
});
// having the export here results in working code, but without the variable being set.
module.exports = foo;
How can I export the module only once the async callback has been executed?
edit
a quick note on my actual use-case: I'm writing a module to configure nconf (https://github.com/flatiron/nconf) in an fs.exists() callback (i.e. it will parse a config file and set up nconf).
Your export can't work because it is outside the function while the foodeclaration is inside. But if you put the export inside, when you use your module you can't be sure the export was defined.
The best way to work with an ansync system is to use callback. You need to export a callback assignation method to get the callback, and call it on the async execution.
Example:
var foo, callback;
async.function(function(response) {
foo = "foobar";
if( typeof callback == 'function' ){
callback(foo);
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback = cb;
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Multiple callback way
If your module need to be called more than once you need to manage an array of callback:
var foo, callbackList = [];
async.function(function(response) {
foo = "foobar";
// You can use all other form of array walk.
for(var i = 0; i < callbackList.length; i++){
callbackList[i](foo)
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback.push(cb);
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Promise way
You can also use Promise to solve that. This method support multiple call by the design of the Promise:
var foo, callback;
module.exports = new Promise(function(resolve, reject){
async.function(function(response) {
foo = "foobar"
resolve(foo);
});
});
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js').then(function(foo){
//Here code using foo;
});
See Promise documentation
An ES7 approach would be an immediatly invoked async function in module.exports :
module.exports = (async function(){
//some async initiallizers
//e.g. await the db module that has the same structure like this
var db = await require("./db");
var foo = "bar";
//resolve the export promise
return {
foo
};
})()
This can be required with await later:
(async function(){
var foo = await require("./theuppercode");
console.log(foo);
})();
ES6 answer using promises:
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// Where someAsyncFunction takes a callback, i.e. api call
someAsyncFunction(data => {
resolve(data)
})
})
}
export default asyncFunc
...
import asyncFunc from './asyncFunc'
asyncFunc().then(data => { console.log(data) })
Or you could return the Promise itself directly:
const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
Another approach would be wrapping the variable inside an object.
var Wrapper = function(){
this.foo = "bar";
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
});
}
module.exports = new Wrapper();
If the initializer has error, at least you still get the uninitialized value instead of hanging callback.
You can also make use of Promises:
some-async-module.js
module.exports = new Promise((resolve, reject) => {
setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000);
});
main.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// outputs 'someValueToBeReturned' after 2 seconds
The same can happen in a different module and will also resolve as expected:
in-some-other-module.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// also outputs 'someValueToBeReturned' after 2 seconds
Note that the promise object is created once then it's cached by node. Each require('./some-async-module') will return the same object instance (promise instance in this case).
Other answers seemed to be partial answers and didn't work for me. This seems to be somewhat complete:
some-module.js
var Wrapper = function(){
this.callbacks = [];
this.foo = null;
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
this.callbacks.forEach(function(callback){
callback(null, wrapper.foo);
});
});
}
Wrapper.prototype.get = function(cb) {
if(typeof cb !== 'function') {
return this.connection; // this could be null so probably just throw
}
if(this.foo) {
return cb(null, this.foo);
}
this.callbacks.push(cb);
}
module.exports = new Wrapper();
main.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined
});
main2.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined in another script
});

Resources