JS Jest: getting undefined error on simple mock test - javascript

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.

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

A sinon.spy function that is called does not state it has been called

I'm trying to mock an EmberJS adapter which has a function that carries out a POST request. My test looks like this:
test('should post something', async function (assert) {
let controller = this.owner.lookup('path/to/ach-deposit');
controller.setProperties({
...,
store: {
adapterFor: () => {
return {postAchDeposit: sinon.spy()}
}
}
})
await controller.actions.postAchDepositHandler.call(controller);
assert.ok(controller.store.adapterFor.call().postAchDeposit.called);
})
This fails. Stepping into the code of where postAchDeposit is called throws no errors. If I were to change sinon.spy() to sinon.stub().return("Hi") it would return Hi but for whatever reason when I try to see if it has been called, it returns false.
In the debugger after postAchDeposit has been called if I check there using this.get('store').adapterFor('funding/ach-deposit').postAchDeposit.called still it returns false.
What am I missing?
each time adapterFor is called sinon.spy() is called again and so a new spy is created.
So essentialls:
controller.store.adapterFor().postAchDeposit !== controller.store.adapterFor().postAchDeposit
You probably should first create your stub and then always pass a reference:
const postAchDeposit = sinon.spy();
controller.setProperties({
...,
store: {
adapterFor: () => {
return {postAchDeposit}
}
}
});
await controller.actions.postAchDepositHandler.call(controller);
assert.ok(postAchDeposit.called);

Function declared within mock is undefined

I have the following code in Jest:
const mockOnNextAction = jest.fn(() => {
then: jest.fn();
});
jest.mock('NativeModules', () => {
return {
MyNativeModule: {
onNextAction: mockOnNextAction
}
};
});
MyNativeModule.executeSomeChecks();
expect(NativeModules.MyNativeModule.onNextAction).toHaveBeenCalled();
expect(NativeModules.MyNativeModule.onNextAction().then).toHaveBeenCalled();
In the code I am testing, I would like to make a call like this one:
NativeModules.MyNativeModule.onNextAction().then(() => {
//contents of callback function
});
The problem is that in the code under test I am getting this error:
TypeError: Cannot read property 'then' of undefined
Also, the following print statement:
console.log("NativeModules.MyNativeModule.onNextAction: " + JSON.stringify(NativeModules.MyNativeModule.onNextAction));
has the result undefined
So why is onNextAction undefined? I am mocking it with Jest so it should exist.
There were two problems with my mock.
1.I was not respecting ES6 lambda function syntax, so I changed to: () => ({...}) and I removed the ; inside the object.
2.I made the then field reference the same variable, otherwise NativeModules.MyNativeModule.onNextAction().then would always be a new object which was not called.
Finally, my code looks like this:
const mockThen = jest.fn();
const mockOnNextAction = jest.fn(() => ({
then: mockThen
}));
jest.mock('NativeModules', () => ({
MyNativeModule: {
onNextAction: mockOnNextAction
}
}));

how can I spy on a function with jest

I have a function that calls another function
const getArtist = (id) => {
// builds params for other function
return otherFuntion(params);
}
I want to verify that when I call getArtist(someValue), otherFunction gets called with the right arguments.
How can I accomplish that with jest ?
describe('getArtist function', () => {
it('should call otherFunction with the right params', () => {
// how can I get a handle on the mock of otherFunction to see
// if it was called correctly ?
});
});
You could use proxyquire to mock out the imported function at compile time if it's a public function. If it's a private function then you definitely don't want to mock it out.
You should probably just test the return value of otherFuntion instead.
So you are really calling the function otherFuntion(params) so I would rather check if return of your getArtist functions gives the same results as otherFunction called with right params.
describe('getArtist function', () => {
it('should call otherFunction with the right params', () => {
expect(getArtist(id)).toBe(otherFunction(params));
});
});
Or create special function for generate params and test it separatly. Like prepareArtistParams and use it inside:
const getArtist = (id) => {
const params = prepareArtistParams(id);
return otherFuntion(params);
}
describe('otherFuntion function', () => {
it('should create right params', () => {
expect(prepareArtistParams(id)).toBe(params);
});
});

spying on functions returned by a function sinon

I'm a bit new to Sinon and having some trouble with the scenario where I need to spy on not only a function, but the functions returned by the function. Specifically, I'm trying to mock the Azure Storage SDK and ensure that once I've created a queue service, that the methods returned by the queue service are also called. Here's the example:
// test.js
// Setup a Sinon sandbox for each test
test.beforeEach(async t => {
sandbox = Sinon.sandbox.create();
});
// Restore the sandbox after each test
test.afterEach(async t => {
sandbox.restore();
});
test.only('creates a message in the queue for the payment summary email', async t => {
// Create a spy on the mock
const queueServiceSpy = sandbox.spy(AzureStorageMock, 'createQueueService');
// Replace the library with the mock
const EmailUtil = Proxyquire('../../lib/email', {
'azure-storage': AzureStorageMock,
'#noCallThru': true
});
await EmailUtil.sendBusinessPaymentSummary();
// Expect that the `createQueueService` method was called
t.true(queueServiceSpy.calledOnce); // true
// Expect that the `createMessage` method returned by
// `createQueueService` is called
t.true(queueServiceSpy.createMessage.calledOnce); // undefined
});
Here's the mock:
const Sinon = require('sinon');
module.exports = {
createQueueService: () => {
return {
createQueueIfNotExists: (queueName) => {
return Promise.resolve(Sinon.spy());
},
createMessage: (queueName, message) => {
return Promise.resolve(Sinon.spy());
}
};
}
};
I'm able to confirm that the queueServiceSpy is called once, but I'm having trouble determining if the methods returned by that method are called (createMessage).
Is there a better way to set this up or am I just missing something?
Thanks!
What you need to do is stub your service function to return a spy that you can then track calls to elsewhere. You can nest this arbitrarily deep (though I would strongly discourage that).
Something like:
const cb = sandbox.spy();
const queueServiceSpy = sandbox.stub(AzureStorageMock, 'createQueueService')
.returns({createMessage() {return cb;}}});
const EmailUtil = Proxyquire('../../lib/email', {
'azure-storage': AzureStorageMock,
'#noCallThru': true
});
await EmailUtil.sendBusinessPaymentSummary();
// Expect that the `createQueueService` method was called
t.true(queueServiceSpy.calledOnce); // true
// Expect that the `createMessage` method returned by
// `createQueueService` is called
t.true(cb.calledOnce);

Categories

Resources