How to mock child function with Jest - javascript

I have a sendMail.js class content as below
export default function registerEmailHandlers() {
const sendMailAsync = async (emailAddress) => {
return await sendMail();
}
return {
sendMailAsync
}
}
How can I write Jest mock for this type of code?
I want mock sendMailAsync always return 'some test'.
I tried to research but nothing same that what I want.

Could you do something like
const mockSendMailAsync = jest.fn());
jest.mock('./path/to/file/sendMail', () => (
sendMailAsync = mockSendMailAsync
)
Then you could assert that mockSendMailAsync has been called

Related

How do I test a function within a React hook, but that inner function returns based on the output of a promise

I have a hook that I want to test that looks something like this:
export useFunc(){
const func = async () => {
const [result, otherStuff] = await Promise.all([getSomeData(), someOtherStuff()])
if (!result){
return; // I NEED TO TEST THIS
}
return result;
}
return { func };
}
The test is set up like so:
it("whatever description", () => {
const { result } = renderHook(() => useFunc());
const { func } = result.current;
// I need to somehow test that the return gets hit inside `func`!
});
How do I go about testing this return statement?
Okay something like this works, as easy as just calling the function like Tom mentioned!
it("whatever description", async () => {
mockGetSomeData.mockResolvedValueOnce(undefined) // just need to make sure this is mocked properly
const { result } = renderHook(() => useFunc());
const { func } = result.current;
const returnedValue = await func();
expect(returnedValue).toBe(undefined);
});

Jest Unit Testing function that calls a second one that returns a promise

Edited Question with vazsonyidl suggestions applied
I have to write unit tests for a function similar to this one:
import {External} from 'ExternalModule';
async functionA(){
this.functionB().then((data) => {
External.functionC(options);
console.log("Reached1");
}).catch((data) => {
const { OnError = "" } = data || {}
if(OnError) {
External.functionC(anotherOptions);
console.log("Reached2");
}
})
}
functionB() {
return new Promise(() => {
});
}
As functionC belongs to another module, I placed a mock of it in the _mocks_folder:
//_mocks_/ExternalModule.ts
export var External: ExternalClass = {
functionC(){}
}
class ExternalClass{
constructor(){};
functionC(){};
}
I have mocked functionB in two diferent ways for testing the then and the catch :
it("should test then block", () => {
functionB = jest.fn(() => {return Promise.resolve()});
const functionSpy = jest.spyOn(ExternalModule.External, 'functionC');
void functionA().then(() => {
expect(functionSpy).not.toHaveBeenCalled();
});
})
it("should test catch block", () => {
const err = { OnError: "Error" };
functionB = jest.fn(() => {return Promise.reject(err)});
const functionSpy = jest.spyOn(ExternalModule.External, 'functionC');
void functionA().then(() => {
expect(functionSpy).not.toHaveBeenCalled();
});
})
What I am trying to do is expect that functionC was called and called with the correct params, but the test is always passing even if I test if functionC was not called.
What am I doing wrong?
Jest does not wait for the async code to complete before doing assertions.
You can use the following function:
const waitForPromises = () => new Promise(setImmediate);
to force Jest to wait for promises to complete before continuing like so:
it("does something", async () => {
promiseCall();
await waitForPromises();
expect(something).toBe(something)
});
I think when this function catch error, this error should have an 'OnError' property so the functionC can run.
const { OnError = "" } = data || {}
if(OnError) {
ExternalClass.functionC(anotherOptions);
}
change you response error data to return Promise.reject({OnError: '404'}) may solve this problem.
Because you are not providing it to your class.
The following code is working for me:
class A {
async functionA() {
this.functionB().then((data) => {
this.functionC(); // It woll log aaa here, you need this one.
}).catch((data) => {
const {OnError = ''} = data || {};
if (OnError) {
console.log('onerror');
}
});
}
functionB() {
return new Promise(() => {
});
}
functionC() {
return 2;
}
}
describe('a', () => {
it('test', () => {
const a = new A();
a.functionB = jest.fn(() => Promise.resolve());
const functionBSpy = jest.spyOn(a, 'functionC');
void a.functionA().then(() => {
expect(functionBSpy).toHaveBeenCalledTimes(1);
});
});
});
Hope this helps, any comment appreciated.
As you provided no information about your functionB I mocked something that may suitable for you.
Your original problem is that Jest does not wait for your callbacks to settle. It does the assertion although, even if your function calls happen later, Jest will not recognise them and says that no call ever occurred.
There are several docs available, for example Jest's one here

Locked it method in chai

I have a js file which supplies some db operations. This file works with promises only which can be chained. To test that class I work with an async function.
The problem is, that whenever I work with promises inside my test function the it function gets blocked for every other test later.
Here are two examples:
'use strict'
const exec = require('child_process').exec
const path = require('path')
const request = require('request')
const expect = require('chai').expect
const createTableStatements = require('../data')
test()
async function test () {
await testGetUser()
console.log('1')
await testGetFaculties()
}
function testGetUser () {
return new Promise((resolve1) => {
describe('test get user', function () {
const db = require('../dbInterface')
it('test get user should be complete', function () {
db.dbFunctions.dropAll()
.then(onResolve => {
return db.dbFunctions.createTable(createTableStatements.createTableStatements.user)
}
)
.then(() => {
console.log('success create user table')
return db.dbFunctions.addUser('1', 'firstName', 'lastName', 'email')
})
.then(resolve => {
return db.dbFunctions.getUser('email', undefined)
})
.then(result => {
expect(result.toString().includes('dummy')).to.equal(false)
})
.then(resolve => {
return db.dbFunctions.dropAll()
})
.then(resolve => {
console.log('resolve')
resolve1()
})
.catch(err => console.log(err))
})
})
})
}
function testGetFaculties () {
return new Promise(resolve => {
describe('test get faculties', function () {
let db
before(function () {
db = require('../dbInterface')
})
console.log('displayed')
it('should work', function () {
console.log('locked')
expect(db.dbFunctions.getFaculties('hsa')).to.be.an('array').that.does.include('Science')
resolve()
})
})
})
}
And this is the output
resolve
1
displayed
As you can see console.log('locked') is not being processed.
What i figured out so far, that I only have this issue when I call expect within a then function. But this is necessary for my tests.
The test () function should contain much more tests, only for this question I shortened it.
And for clarification: If I only test methods type of testGetFaculties () which don't contains another promise chain inside it works like it should.
Any idea why this is like it is?
Most probably the console.log( 'locked' ); doesn't do anything, because your previous test case was not finished at all.
Writing describe, it, before inside a Promise and containing unreturned Promises is something that you should not do.
Much better test case would look like :
'use strict'
const exec = require('child_process').exec
const path = require('path')
const request = require('request')
const expect = require('chai').expect
const createTableStatements = require('../data')
// You use this in both test cases anyway
const db = require('../dbInterface');
describe('test get user', function () {
it('test get user should be complete', function () {
return db
// ^ returning promise will make sure that the test ends when the promise ends.
.dbFunctions
.dropAll()
.then(onResolve => { ... } )
...
)
} );
} );
describe('test get faculties', function () {
it('should work', function () {
return db
// ^ returning promise will make sure that the test ends when the promise ends.
.dbFunctions
.getFaculties('hsa')
.then( value => {
// ^ You actually need to test the value of the resolve promise
expect( value ).to.be.an('array').that.does.include('Science');
} )
} );
} );

Jest spyOn() calls the actual function instead of the mocked

I'm testing apiMiddleware that calls its helper function callApi. To prevent the call to actual callApi which will issue the API call, I mocked the function. However, it still gets called.
apiMiddleware.js
import axios from 'axios';
export const CALL_API = 'Call API';
export const callApi = (...arg) => {
return axios(...arg)
.then( /*handle success*/ )
.catch( /*handle error*/ );
};
export default store => next => action => {
// determine whether to execute this middleware
const callAPI = action[CALL_API];
if (typeof callAPI === 'undefined') {
return next(action)
}
return callAPI(...callAPI)
.then( /*handle success*/ )
.catch( /*handle error*/ );
}
apiMiddleware.spec.js
import * as apiMiddleware from './apiMiddleware';
const { CALL_API, default: middleware, callApi } = apiMiddleware;
describe('Api Middleware', () => {
const store = {getState: jest.fn()};
const next = jest.fn();
let action;
beforeEach(() => {
// clear the result of the previous calls
next.mockClear();
// action that trigger apiMiddleware
action = {
[CALL_API]: {
// list of properties that change from test to test
}
};
});
it('calls mocked version of `callApi', () => {
const callApi = jest.spyOn(apiMiddleware, 'callApi').mockReturnValue(Promise.resolve());
// error point: middleware() calls the actual `callApi()`
middleware(store)(next)(action);
// assertion
});
});
Please ignore the action's properties and argument of callApi function. I don't think they are the concern of the point I'm trying to make.
Tell me if you need further elaboration.
The jest mocking only works on imported functions. In your apiMiddleware.js the default function is calling callApi variable, not the "exported" callApi function. To make the mock work, move callApi into its own module, and import it in apiMiddleware.js
Good question!
I solved my issues converting my code to a Class, example:
// Implementation
export class Location {
getLocation() {
const environment = this.getEnvironmentVariable();
return environment === "1" ? "USA" : "GLOBAL";
}
getEnvironmentVariable() {
return process.env.REACT_APP_LOCATION;
}
}
// Test
import { Location } from "./config";
test('location', () => {
const config = new Location();
jest.spyOn(config, "getEnvironmentVariable").mockReturnValue("1")
const location = config.getLocation();
expect(location).toBe("USA");
});

sinon mock not catching calls

I am having a hard time understanding what I am doing wrong.
I have a JS class as such:
export default class A {
constructor(repository) {
this._repository = repository;
}
async process(date) {
// ...
this._repository.writeToTable(entry);
}
}
and I am attempting to write a test that mocks the repository using sinon.mock
This is what I have so far:
describe('A', () => {
describe('#process(date)', () => {
it('should work', async () => {
const repository = { writeToTable: () => {} };
const mock = sinon.mock(repository);
const a = new A(repository);
await a.process('2017-06-16');
mock.expects('writeToTable').once();
mock.verify();
});
});
});
but it always fails saying that
ExpectationError: Expected writeToTable([...]) once (never called)
I've checked (added a console.log) and it is calling the object I defined on the test.
I ran this locally and read the documentation on sinonjs.org and you seem to be doing everything right.
I tried re-writing your example using a spy and ended up with something like this to get a passing test:
import sinon from "sinon";
import { expect } from "chai";
import A from "./index.js";
describe("A", () => {
describe("#process(date)", () => {
it("should work", async () => {
const repository = { writeToTable: sinon.spy() };
const a = new A(repository);
await a.process("2017-06-16");
expect(repository.writeToTable.calledOnce).to.be.true;
});
});
});

Categories