I am new to the React and Jest and I am trying to test my function where : I have a function :
handleSubmit = (e) => {
this.changeData().then(() => {
const payload = {
somePayload: 'sample'
};
this.props.actions.authenticate(payload);
});
};
and the jest function as :
it('test mock test', () => {
const actionsMock = { authenticate: jest.fn() };
const localWrapper = shallow(<SomeComponent actions={actionsMock} />);
const instance = localWrapper.instance();
jest.spyOn(instance, 'handleSubmit');
instance.forceUpdate();
localWrapper.find('.button').simulate('click');
expect(actionsMock.authenticate).toHaveBeenCalledWith({somePayload: 'sample'});
});
So, In this case, when I click on the button it calls the handlesubmit function and eventually the this.props.actions.authenticate(payload); but when I assert the same in Jest it gives me error that function was never called.
EDIT
As in the comment CJ Pointed: I see that my assertion is getting called even before the promise for changeData resolved. So, How I can I make my assertion wait till the promise gets resolved?
Related
I could not write unit tests with a jest for my class function that uses 'reaction' that comes from the 'mobx' library.
I was trying to test the init() function shown below.
async init() {
reaction(
() => this.repositoryManager.selectedClient?.repositoryMeta,
(repositoryMeta) => {
clearInterval(this.incidentsTimer);
clearInterval(this.anomaliesTimer);
if (!repositoryMeta) return;
this.getOpenIncidents();
this.getOpenAnomalies();
this.incidentsTimer = setInterval(this.getOpenIncidents, 60 * 1000);
this.anomaliesTimer = setInterval(this.getOpenAnomalies, 60 * 1000);
}
);
}
To achieve that, I have used jest mock like this :
import { makeAutoObservable, reaction } from 'mobx';
jest.mock('mobx', () => ({
makeAutoObservable: jest.fn(),
reaction: jest.fn(),
}));
This test passes without a problem.
test('should call the reaction method with the correct arguments', async () => {
await newRelicManager.init();
expect(reaction).toHaveBeenCalledWith(expect.any(Function), expect.any(Function));
});
But if I want to test my logic, that is inside a reaction like this.
test('should call the getOpenIncidents and getOpenAnomalies methods', async () => {
// Arrange
const getOpenIncidentsSpy = jest.spyOn(newRelicManager, 'getOpenIncidents');
const getOpenAnomaliesSpy = jest.spyOn(newRelicManager, 'getOpenAnomalies');
const setIntervalSpy = jest.spyOn(global, 'setInterval');
newRelicManager.repositoryManager = {
selectedClient: {
repositoryMeta: {
name: 'some-repository-name',
},
},
} as any;
// Act
await newRelicManager.init();
// Assert
expect(getOpenIncidentsSpy).toHaveBeenCalled();
expect(getOpenAnomaliesSpy).toHaveBeenCalled();
expect(setIntervalSpy).toHaveBeenCalledTimes(2);
expect(newRelicManager.incidentsTimer).toBeDefined();
expect(newRelicManager.anomaliesTimer).toBeDefined();
});
It throws:
Error: expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
So even if repositoryMeta is set, it can't simulate inside of the reaction. So what is the best way to test mobx reaction?
Solution:
To trigger the callback function, that is the parameter of the reaction method. jest.spyOn() should be used instead of jest.fn() like this:
makeAutoObservable = jest.spyOn(mobx, 'makeAutoObservable');
reaction = jest.spyOn(mobx, 'reaction');
And it should be triggered in the test like this.
await newRelicManager.init();
reaction.mock.calls[0][1]('repositoryMeta');
I have a function that receives another function as an argument. I would like to make sure it was called properly.
Function to be tested:
const loadNamespaces = (setNamespaces) => {
namespaceAPI.getNamespaces().then(namespaces => {
setNamespaces(namespaces);
});
}
My main goal here was to assert mockSetNamespaces was called.
I was able to mock and assert namespaceAPI.getNamespaces was called by using jest.spyOn method, but that didn't work for asserting if mockSetNamespaces was called:
test("loadNamespaces", () => {
const mockSetNamespaces = jest.fn();
const mockNamespaces = [
{ endpoint: "mock namespace 1", rooms: [] },
];
jest.spyOn(namespaceAPI, "getNamespaces").mockImplementation(() => {
return new Promise((resolve) => {
resolve(mockNamespaces);
});
});
SocketIOActions.loadNamespaces(mockSetNamespaces);
expect(namespaceAPI.getNamespaces).toHaveBeenCalled();
expect(mockSetNamespaces).toHaveBeenCalled();
});
Error message received from Jest:
● loadNamespaces
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
I've also tried to add setNamespaces to an object, so jest.spyOn method could be used, but also didn't assert method was called:
test("loadNamespaces", () => {
const mockObject = {
mockSetNamespaces: jest.fn(),
};
const mockNamespaces = [
{ endpoint: "mock namespace 1", rooms: [] },
];
jest.spyOn(namespaceAPI, "getNamespaces").mockImplementation(() => {
return new Promise((resolve) => {
resolve(mockNamespaces);
});
});
jest.spyOn(mockObject, "mockSetNamespaces").mockImplementation(() => {
console.log("Hello from spy function");
});
SocketIOActions.loadNamespaces(mockObject.mockSetNamespaces);
expect(namespaceAPI.getNamespaces).toHaveBeenCalled();
expect(mockObject.mockSetNamespaces).toHaveBeenCalled();
});
Proof that mock function was actually called:
console.log
Hello from spy function
Is this the expected behavior from Jest? I would be glad to know if there is a cleaner way to do this.
Using spyOn when you need to mock specific function from the module instead of mocking all.
I would do in this way.
// this will help you auto mock all namespaceAPI function. If you just need to mock "getNamespaces" then you stick with spyOn
jest.mock('namespaceAPI')
test("loadNamespaces", () => {
// you can directly mock implementation in jest function, dont need to spy it again.
const mockSetNamespaces = jest.fn().mockImplementation(() => {
console.log("Hello from spy function");
});
SocketIOActions.loadNamespaces(mockSetNamespaces);
expect(namespaceAPI.getNamespaces).toHaveBeenCalled();
expect(mockSetNamespaces).toHaveBeenCalled();
});
I'm using TypeScript to write a very simple service that utilizes the AWS SDK. My Jest unit tests are passing, but the coverage reports are saying that the line 'return result.Items' is not covered. Can anyone tell why this is? Is it a bug in jest?
// service file
/**
* Gets an array of documents.
*/
function list(tableName) {
const params = {
TableName: tableName,
};
return docClient
.scan(params)
.promise()
.then((result) => {
return result.Items;
});
}
// test file
const stubAwsRequestWithFakeArrayReturn = () => {
return {
promise: () => {
return { then: () => ({ Items: 'fake-value' }) };
},
};
};
it(`should call docClient.scan() at least once`, () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
db.list('fake-table');
expect(mockAwsCall).toBeCalledTimes(1);
});
it(`should call docClient.scan() with the proper params`, () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
db.list('fake-table');
expect(mockAwsCall).toBeCalledWith({
TableName: 'fake-table',
});
});
it('should return result.Items out of result', async () => {
const mockAwsCall = jest
.fn()
.mockImplementation(stubAwsRequestWithFakeArrayReturn);
aws.docClient.get = mockAwsCall;
const returnValue = await db.get('fake-table', 'fake-id');
expect(returnValue).toEqual({ Items: 'fake-value' });
});
The line not covered is the success callback passed to then.
Your mock replaces then with a function that doesn't accept any parameters and just returns an object. The callback from your code is passed to the then mock during the test but it doesn't call the callback so Jest correctly reports that the callback is not covered by your tests.
Instead of trying to return a mock object that looks like a Promise, just return an actual resolved Promise from your mock:
const stubAwsRequestWithFakeArrayReturn = () => ({
promise: () => Promise.resolve({ Items: 'fake-value' })
});
...that way then will still be the actual Promise.prototype.then and your callback will be called as expected.
You should also await the returned Promise to ensure that the callback has been called before the test completes:
it(`should call docClient.scan() at least once`, async () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
await db.list('fake-table'); // await the Promise
expect(mockAwsCall).toBeCalledTimes(1);
});
it(`should call docClient.scan() with the proper params`, async () => {
const mockAwsCall = jest.fn().mockImplementation(stubAwsRequest);
aws.docClient.scan = mockAwsCall;
await db.list('fake-table'); // await the Promise
expect(mockAwsCall).toBeCalledWith({
TableName: 'fake-table',
});
});
The Library chai-as-promised is worth looking at.
https://www.chaijs.com/plugins/chai-as-promised/
Instead of manually wiring up your expectations to a promise’s
fulfilled and rejected handlers.
doSomethingAsync().then(
function (result) {
result.should.equal("foo");
done();
},
function (err) {
done(err);
}
);
you can write code that expresses what you really mean:
return doSomethingAsync().should.eventually.equal("foo");
I am trying to mock a property of an object that acts as an object and as a function at the same time. Here's the code:
index.js
const nock = require('nock');
async function myFunc() {
nock.back.setMode('param1');
const { nockDone } = await nock.back('param1', 'param2');
nock.enableNetConnect('param1');
return nockDone;
}
module.exports = { myFunc }
My goal is to mock the nock object in a way I can assert that when myFunc is called, nock.back is called with param1 and param2.
To do so I have the following test:
index.test.js
const nock = require('nock');
const subjectUnderTest = require('./index');
const nockBackImplementation = jest.fn();
nockBackImplementation.setMode = jest.fn();
const nockBackMock = jest.spyOn(nock, 'back');
nockBackMock.mockImplementation(() => nockBackImplementation);
describe('test', () => {
it('calls nock.back with the proper parameters', () => {
subjectUnderTest.myFunc();
expect(nockBackMock).toHaveBeenCalledWith('param1', 'param2');
});
});
For some reason, the test fails saying that the mock function has not been called and also gives this error:
UnhandledPromiseRejectionWarning: TypeError: nock.back.setMode is not a function
I'm not sure how to properly mock nock.
You're assigning the setMode mock to the function used as implementation, which I think is different from the actual nock.back mock. You can set it correctly this way
const nockBackMock = jest
.spyOn(nock, "back")
.mockResolvedValue({ nockDone: "test" });
nockBackMock.setMode = jest.fn();
// you also need to mock the enableNetConnect to avoid errors
const enableNetConnectMock = jest
.spyOn(nock, "enableNetConnect")
.mockReturnValue({});
describe("test", () => {
it("calls nock.back with the proper parameters", () => {
subjectUnderTest.myFunc();
expect(nockBackMock).toHaveBeenCalledWith("param1", "param2");
});
});
I am currently trying to write integration test to make sure that some specific client cases which have caused issues are not broken with further changes to a script. I am not sure how to properly format the test so that it runs and waits for the function to finish before returning:
Your test suite must contain at least one test.
The system calls the appropriate functions when mocked.
// app.ts
function doThing() {
const data = window.data;
if (!isEmpty(data)) {
firstAsync(value1 => {
return secondAsync(value1);
}).then(value2 => {
someCall();
}).catch(error => {
log(error);
});
} else {
someOtherCall();
}
}
// app.test.ts
someCall = jest.fn();
someOtherCall = jest.fn();
describe('test', () => {
it('should work', () => {
window.data = { value: 'something' };
doThing();
expect(someCall).toHaveBeenCalled();
expect(someOtherCall).not.toHaveBeenCalled();
});
});
TL:DR - How would I write a test to cover a function that has a promise inside of it, but isn't a promise itself?