Unable to access Variable value using nodejs exports? - javascript

Hey i have fallen into a situation where i cannot access a variable which i have set within a nodejs module which i have exposed with module exports to access from the main file, i will show you below:
Login.js:
let DEVICES;
function a() {
return DEVICES;
}
async function init() {
try {
const __devices = await _db_get_devices();
DEVICES = new DeviceCollection(__devices);
console.log(a()) <-- **Returns the object correctly**
} finally {
console.log("Login Initialised!");
}
}
module.exports = { init, a }
Below is the code that is having issues:
App.js
const Login = require('./func/Login');
Login.init() <-- **runs init function no issues**
console.log(Login.a()); <-- **returns undefined**
I have figured that its something to do with async but that is why i setup a function to call it later so not sure if there is a better method to calling the variable when its set.

init is an async function, so following statement
console.log(Login.a())
will run before init function has executed completely. Hence you get undefined because DEVICES isn't initialized yet.
You can return DEVICES from the init function and call init function as shown below
Login.init()
.then(data => console.log(data)) // log the return value of init function
.catch(error => console.log(error);
or you could call function a after init function has executed completely
Login.init()
.then(() => console.log(Login.a()))
.catch(error => console.log(error);

Related

Why isn't my jest mock function implementation being called?

I have the following jest test configuration for my collection of AWS JS Node Lambdas. I have a module called dynamoStore I reference in several different lambdas package.json and use within the lambdas. I am trying to get test one of these lambdas by mocking the dynamo store module as it makes calls to dynamoDb. The problem is that the jest.fn implementation never gets called. I confirmed this by sticking a breakpoint in that line as well as logging the value the calling methods returns from it.
When I check lambda1/index.js in the debugger getVehicleMetaKeysFromDeviceId() is a jest object but when it is called it doesn't use my mock implementation
How do I get this implementation to work? Have I set up my mock incorrectly?
dynamoStore/vehicleMetaConstraints
exports.getVehicleMetaKeysFromDeviceId= async (data) => {
return data
};
dynamoStore/index.js
exports.vehicleMetaConstraints = require("./vehicleMetaConstraints");
...
lambda1/index.js
const { vehicleMetaStore } = require("dynamo-store");
exports.handler = async (event, context, callback) => {
const message = event;
let vehicle_ids = await vehicleMetaStore.getVehicleMetaKeysFromDeviceId(message.id);
// vehicle_ids end up undefined when running the test
}
lambda1/index.test.js
const { vehicleMetaStore } = require("dynamo-store");
jest.mock("dynamo-store", () => {
return {
vehicleMetaStore: {
getVehicleMetaKeysFromDeviceId: jest.fn(),
},
};
});
describe("VehicleStorageLambda", () => {
beforeEach(() => {
jest.resetModules();
process.env = { ...env };
});
afterEach(() => {
jest.clearAllMocks();
});
test("Handles first time publish with existing device", async () => {
let functionHandler = require("./index");
vehicleMetaStore.getVehicleMetaKeysFromDeviceId.mockImplementationOnce(() =>
// This never gets called
Promise.resolve({
device_id: "333936303238510e00210022",
})
);
await functionHandler.handler({});
});
});
Remove the call to jest.resetModules() in beforeEach. That's re-importing your modules before each test, and wiping out your mocks.
https://stackoverflow.com/a/59792748/3084820

JS Jest: getting undefined error on simple mock test

I have a simple function that takes a player and calls a nested function setPlayer() on it:
myFunction = (player) => {
player.getData().setPlayer(true)
}
main.test.js
it('expects setPlayer to be called', () => {
player = jest.fn();
// mocking the myFunction
player.getData = jest.fn(() => {
return {
setPlayer: jest.fn().mockReturnValue(true),
}
})
myFunction(player) // calling function
expects(player.getData.setPlayer).toBeCalled();
});
I keep getting this error in my test though but I don't understand why. I seem to have everything mocked unless I am forgetting something:
expect(received).toBeCalled()
Matcher error: received value must be a mock or spy function
Received has value: undefined
I keep getting this error in my test though but I don't understand why.
player.getData is a mock function. That function doesn't have a setPlayer property. You are passing undefined to expects, hence the error.
Calling player.getData would return you an object with a setPlayer function, but you can't just do expects(player.getData().setPlayer) because that would return a different setPlayer mock function than the one called by myFunction.
Instead you simply create the setPlayer mock function up front and reference it directly. I also cleaned up the code a little.
it('expects setPlayer to be called', () => {
const setPlayerMock = jest.fn().mockReturnValue(true);
const player = {
getData: jest.fn(() => {
return {
setPlayer: setPlayerMock,
};
}),
};
myFunction(player); // calling function
expects(setPlayerMock).toBeCalled();
});
FWIW, with the information you have given, it wouldn't be necessary to use a mock function for getData, you could just do
const player = {
getData() {
return {setPlayer: setPlayerMock};
},
}
You only need to use mock functions when you want to make assertions on them.

Functions not firing in correct order with async / await

In an Angular service, I have created the following function:
getListKey(user) {
firebase.database().ref(`userprofile/${user.uid}/list`).once('value').then(snapshot => {
console.log(snapshot.val())
this.listKey = snapshot.val()
return this.listKey
})
}
I want to call this function in another file on load, and assign the value brought back to a global listKey variable in the service to be used for another function from the component. However, the second function is firing before the data has been retrieved even with using async/await.
This is the relevant piece from my component:
this.afAuth.authState.subscribe(async (user: firebase.User) => {
await this.fire.getListKey(user);
this.fire.getUserList(this.fire.listKey).subscribe(lists => {...})
...
}
How can I make getUserList() wait for listKey?
Add a return statement to getListKey to return the promise. Otherwise, you're returning undefined, and awaiting undefined will not wait for the database snapshot to be ready.
getListKey(user) {
return firebase.database().ref(`userprofile/${user.uid}/list`).once('value').then(snapshot => {
console.log(snapshot.val())
this.listKey = snapshot.val()
return this.listKey
})
}
Also, you probably want a left side to your await:
this.afAuth.authState.subscribe(async (user: firebase.User) => {
const listKey = await this.fire.getListKey(user);
this.fire.getUserList(listKey).subscribe(lists => {...})
...
}

Stubbing function to return something acceptable for .promise()

I'm running tests and I'm stubbing a function that calls the AWS sqs.deleteMessage function. .promise() is called on the call to this function. Every time I run my tests with the coverage I notice that it jumps to the catch block thus an error must be occurring on my .promise() call.
I've tried stubbing the function to resolve the promise but that doesn't seem to work. I've tried returning data as well and still have the same issue.
Below is an example of the code I'm trying to test. It never reaches the logger.info() line
fooObj.js
const foo = async (req) => {
try{
let res = await bar.deleteMessage(handle).promise();
logger.info("Sqs result message " + JSON.stringify(res));
} catch(error){
#catch block code
}
}
Below is the code for bar.deleteMessage()
bar.js
const aws = require('aws-sdk');
const sqs = new aws.SQS();
deleteMessage = function(handle){
return sqs.deleteMessage({
ReceiptHandle: handle
});
}
And finally here is the test code
const fooObj = require('foo')
const barObj = require('bar')
jest.mock('bar')
describe('foo test', ()=>{
test('a test' , ()=>{
barObj.deleteMessage.mockImplementation(()=>{
return Promise.resolve({status:200})
});
return fooObj.foo(req).then(data=>{
#Expect statements here
})
}
}
So I would like the logger.info line to be reached in coverage but I assume the issue has to do with how I'm stubbing the bar.deleteMessage function. I could use the aws-sdk-mock but I feel like I'm violating unit testing principles by mocking the sqs call that is in another file and the proper way to do it would simply be to properly stub the bar.deletemessage() function
You just need one change:
bar.deleteMessage needs to return an object with a promise property set to the function that returns the Promise:
barObj.deleteMessage.mockImplementation(() => ({
promise: () => Promise.resolve({ status: 200 })
}));
...or you can shorten it to this if you want:
barObj.deleteMessage.mockReturnValue({
promise: () => Promise.resolve({ status: 200 })
});

Mocking an ES6 class function with Jest

I have a question about how can I mock an ES6 class instance using Jest which is used by the method I actually want to test.
My real case is trying to test a Redux async action creator that make a request and dispatch some action depending on the request result.
This is a simplified example of the use case :
// communication.js
// An exported ES6 class module with proxy to the request library.
import post from './post';
export default class communication {
getData(data, success, error) {
const res = post(data);
if(res) {
success();
} else {
error();
}
}
}
// communicatorAssist.js
// A redux async function using communication.js
import communication from './communication';
// ...
export function retrieveData() {
return dispatch => {
const data = { name: 'michel'};
communication.getData(data,
(res) => dispatch(successAction(res)),
(res) => dispatch(errorAction(res));
}
}
// communicatorAssist.test.js testing the communicatorAssist
import { retrieveData } from 'communicatorAssist';
// communication.getData should call success callback
// for this test.
it('Should call the success callback', () => {
retrieveData();
// Assert that mocked redux store contains actions
});
// communication.getData should call error callback
// for this test.
it('Should call the error callback', () => {
retrieveData();
// Assert that mocked redux store contains actions
});
What I want is mocking the communication class in the test and change the behaviour of the getData() function in each test to call success and error callbacks without any call to post method.
I only success to mock the getData() function for the whole test file with this snippet at the top of it :
import communication from '../communication'
jest.mock('../communication', () => (() => ({
getData: (success, error) => success()
})));
but I can't switch between implementation in different test case.
I figure that something using .mockImplementation() could do the stuff but I can't make this work in my case (I saw examples using it for module exporting functions but not for classes ).
Does anyone have an idea ?
Edit :
I forgot a part in the code exemple : the communication class instance creation which I think is the "problem" for mocking it :
const com = new communication();
If com is instanciated at a global level in the communicatorAssist.js file : it fail with communication.getData is not a function error.
But if I set the instanciation inside the retrieveData() function, Andreas Köberle snippet work's fine :
import communication from '../communication'
jest.mock('../communication', () => jest.fn());
communication.mockImplementation(
() => ({
getData: (success, error) => success()
})
)
(jest.mock() factory parameter need to return a function not directly jest.fn)
I don't know why it's not working using a file global scope instance.
You need to mock the module with jest.fn() then you can import it and change the behaviour of it using mockImplementation:
import communication from '../communication'
jest.mock('../communication', jest.fn());
communication.mockImplementation(
() => ({
getData: (success, error) => success()
})
)

Categories

Resources