Using jest.unmock to test a Promise - javascript

I am using Auth0 to manage authentication for my React App. Here's the function that I want to test:
login(username: string, password: string) {
return new Promise((resolve, reject) => {
this.auth0.client.login({
realm: 'Username-Password-Authentication',
username,
password,
}, (err, authResult) => {
if (err) {
reject(err);
}
if (authResult && authResult.idToken && authResult.accessToken) {
resolve(authResult.idToken);
}
});
});
}
auth0 is instantiated in the following manner:
constructor(clientID: string, domain: string, redirectUri: string) {
// Configure auth0
this.auth0 = new auth0.WebAuth({
clientID,
domain,
responseType: 'token id_token',
redirectUri,
});
}
I have the following two tests:
I want to see if I can instantiate the AuthService class.
I want to see if I can login using a username/password combination
Here's the test file that I wrote:
jest.unmock('../AuthService');
import AuthService from '../AuthService';
describe('Auth0 Library', () => {
test('Should be able to instantiate', () => {
const auth0 = new AuthService('clientID', 'domain');
expect(auth0).toEqual(expect.anything());
});
});
describe('Auth0 Login', () => {
test('Fetch token for existing user', () => {
const auth0 = new AuthService('clientID', 'domain');
auth0.login('email', 'pw')
.then((idToken) => {
console.log('idToken ', idToken);
expect(auth0).toEqual(expect.anything());
});
});
});
The first test runs as expected. However, the second test never works. The promise that is returned is seemingly never resolved, and I never see the console.log.
Could someone please explain to me what I am doing wrong? I am fairly new when it comes to writing jest tests.

Here is the unit test solution for using Promise and error-first callback together.
Folder structure:
.
├── AuthService.spec.ts
├── AuthService.ts
└── auth0
├── WebAuth.ts
└── index.ts
1 directory, 4 files
E.g.
AuthService.ts:
import * as auth0 from './auth0';
export class AuthService {
private auth0: any;
constructor(clientID: string, domain: string, redirectUri: string) {
this.auth0 = new auth0.WebAuth({
clientID,
domain,
responseType: 'token id_token',
redirectUri
});
}
public login(username: string, password: string) {
return new Promise((resolve, reject) => {
this.auth0.client.login(
{
realm: 'Username-Password-Authentication',
username,
password
},
(err, authResult) => {
if (err) {
reject(err);
}
if (authResult && authResult.idToken && authResult.accessToken) {
resolve(authResult.idToken);
}
}
);
});
}
}
auth0/WebAuth.ts:
export class WebAuth {
public client = {
login(options, callback) {
callback(null, {});
}
};
constructor(...args: any) {}
}
auth0/index.ts:
export * from './WebAuth';
AuthService.spec.ts:
import { AuthService } from './AuthService';
const authService = new AuthService('clientid', 'domain', 'redirectUri');
describe('AuthService', () => {
describe('#login', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should login successfully and return id token', async done => {
let loginCallback;
jest.spyOn(authService['auth0']['client'], 'login').mockImplementation((options, callback) => {
loginCallback = callback;
done();
});
const actualValue = authService.login('username', 'password');
const mAuthResult = { idToken: '123', accessToken: '321' };
loginCallback(null, mAuthResult);
await expect(actualValue).resolves.toBe('123');
});
it('should login failed', async done => {
let loginCallback;
jest.spyOn(authService['auth0']['client'], 'login').mockImplementation((options, callback) => {
loginCallback = callback;
done();
});
const actualValue = authService.login('username', 'password');
const mError = new Error('network error');
loginCallback(mError, null);
await expect(actualValue).rejects.toThrowError(mError);
});
});
});
Unit test result with 100% coverage for AuthService.ts:
PASS src/stackoverflow/42137891/AuthService.spec.ts
AuthService
#login
✓ should login successfully and return id token (5ms)
✓ should login failed (2ms)
---------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------|----------|----------|----------|----------|-------------------|
All files | 95 | 100 | 87.5 | 94.12 | |
42137891-todo | 100 | 100 | 100 | 100 | |
AuthService.ts | 100 | 100 | 100 | 100 | |
42137891-todo/auth0 | 85.71 | 100 | 66.67 | 83.33 | |
WebAuth.ts | 83.33 | 100 | 66.67 | 80 | 4 |
index.ts | 100 | 100 | 100 | 100 | |
---------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.728s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/42137891

Related

is there a way to mock the the Promise.resolve in a function

So I tried to mock a jwt.verify function. below is the function itself and then the mock.
try {
const decodedJwt = new Promise((resolve, reject) => {
jwt.verify(
token,
getPublicKey,
{
algorithms: ['RS256'],
ignoreExpiration: false,
},
(decodeError, decodedValue) => {
if (decodeError) {
reject(decodeError);
}
resolve(decodedValue);
}
);
});
return await decodedJwt;
} catch (error) {
logger.error(error);
return null;
}
The Mock for the verify part
export const jwtVerifyMock = jest.fn();
jest.mock('jsonwebtoken', () => {
return {
decode: jwtDecodeMock,
verify: jwtVerifyMock,
};
});
then this is how I use it in my test
it('decodeJWT should should verify token', async () => {
jwtVerifyMock.mockImplementation(async () => Promise.resolve({ exp: config.exp }));
return decodeJwt(config.jwtToken, true).then(async (value) => {
console.log('payload2', value);
const payload = value as Record<string, unknown>;
expect(payload.exp).toEqual(config.exp);
});
});
But for some reason, it doesn't get resolved. and I get a
Async callback was not invoked within the 60000 ms timeout Error
I tried using mockReturn value but that doesn't work as well. So the next thing I'm guessing is that the resolve function that was passed as a callback to the jwt.verify is where my problem lie.
You should mock the implementation of jwt.verify() and execute the callback function manually with different arguments.
E.g.
decode.ts:
import jwt from 'jsonwebtoken';
export async function decode(token, getPublicKey) {
try {
const decodedJwt = new Promise((resolve, reject) => {
jwt.verify(
token,
getPublicKey,
{
algorithms: ['RS256'],
ignoreExpiration: false,
},
(decodeError, decodedValue) => {
if (decodeError) {
reject(decodeError);
}
resolve(decodedValue);
}
);
});
return await decodedJwt;
} catch (error) {
console.error(error);
return null;
}
}
decode.test.ts:
import { decode } from './decode';
import jwt, { JsonWebTokenError } from 'jsonwebtoken';
describe('66966317', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should decode value', async () => {
const decodedvalue = { name: 'teresa teng' };
const verifySpy = jest.spyOn(jwt, 'verify').mockImplementationOnce((token, getPublicKey, options, callback) => {
callback!(null, decodedvalue);
});
const actual = await decode('teresa teng', true);
expect(actual).toEqual({ name: 'teresa teng' });
expect(verifySpy).toBeCalledWith(
'teresa teng',
true as any,
{ algorithms: ['RS256'], ignoreExpiration: false },
expect.any(Function)
);
});
it('should handle decode error', async () => {
const decodedError = new JsonWebTokenError('network');
const verifySpy = jest.spyOn(jwt, 'verify').mockImplementationOnce((token, getPublicKey, options, callback) => {
callback!(decodedError, undefined);
});
const actual = await decode('teresa teng', true);
expect(actual).toBeNull();
expect(verifySpy).toBeCalledWith(
'teresa teng',
true as any,
{ algorithms: ['RS256'], ignoreExpiration: false },
expect.any(Function)
);
});
});
unit test result:
PASS examples/66966317/decode.test.ts (7.701 s)
66966317
✓ should decode value (3 ms)
✓ should handle decode error (17 ms)
console.error
JsonWebTokenError { name: 'JsonWebTokenError', message: 'network' }
23 | return await decodedJwt;
24 | } catch (error) {
> 25 | console.error(error);
| ^
26 | return null;
27 | }
28 | }
at Object.<anonymous> (examples/66966317/decode.ts:25:13)
at Generator.throw (<anonymous>)
at rejected (examples/66966317/decode.ts:1046:32)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
decode.ts | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.749 s

How to mock external function call in jest test

Here is my Node.js module to test (userdata.js file):
const myService = require('./my-service')
class UserData {
getData(query) {
return new Promise((resolve, reject) => {
myService.getUserData(query)
.then((userdata) => {
myService.getManagerData(userdata)
.then((managerdata) => {
resolve(managerdata)
})
})
})
}
}
module.exports = UserData
and test file:
const UserData = require('/.userdata')
test('UserData.getData', () => {
expect(UserData.getData('John Smith')).toBe('John\' manager');
});
I want to mock myService.getUserData and/or myService.getManagerData functions call that I'm not directly declare in test file.. How do I mock them properly to return some dummy result?
You can use jest.spyOn(object, methodName) to make stub for myService.getUserData and myService. getManagerData methods.
E.g.
userData.js:
const myService = require('./myService');
class UserData {
getData(query) {
return new Promise((resolve, reject) => {
myService.getUserData(query).then((userdata) => {
myService.getManagerData(userdata).then((managerdata) => {
resolve(managerdata);
});
});
});
}
}
module.exports = UserData;
myService.js:
const myService = {
getUserData(query) {
return 'real user data';
},
getManagerData(userdata) {
return 'real manager data';
},
};
module.exports = myService;
userData.test.js:
const UserData = require('./userData');
const myService = require('./myService');
describe('61784452', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should pass', async () => {
jest.spyOn(myService, 'getUserData').mockResolvedValueOnce('fake user data');
jest.spyOn(myService, 'getManagerData').mockResolvedValueOnce('fake manager data');
const userData = new UserData();
const actual = await userData.getData('query');
expect(actual).toEqual('fake manager data');
expect(myService.getUserData).toBeCalledWith('query');
expect(myService.getManagerData).toBeCalledWith('fake user data');
});
});
unit test results with coverage report:
PASS stackoverflow/61784452/userData.test.js (12.908s)
61784452
✓ should pass (5ms)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 84.62 | 100 | 71.43 | 84.62 |
myService.js | 50 | 100 | 0 | 50 | 3-6
userData.js | 100 | 100 | 100 | 100 |
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.99s

Jest: How to test the error routes in my code

I have the following function within my ES6 User class which searches for a user given a string.
// Search for a user by their pNick, includes partial matching
static getBypNick(pNick = '') {
// Define our search criteria regex and normalise to lower case
const userSearchRegex = new RegExp(`^${pNick.toLowerCase()}`, 'i')
return new Promise((resolve, reject) => {
// Search user collection for pNick using regex like search and return array of results
userTable.find({
_pNick: userSearchRegex
}).sort({
_pNick: 1
}).exec(function (err, result) {
// If error reject
if (err) {
return reject(err)
}
const userArray = result.map((user) => {
return new User(
user._pNick,
user._firstName,
user._userName,
user._phoneNumber,
user._userID)
})
// Return user records if found
return resolve(userArray)
})
})
}
Whilst I can easily test the success routes using Jest I'm struggling to understand how I can invoke the error cases, especially around the .exec method within the function to invoke my reject routes in the promise.
I understand that I can use various Jest features such as mockImplementation but I just can't figure out the best case in this scenario. The database being used behind the scenes is NeDB, I'm pretty positive I just need to force the .exec portion to return an error and then I should be catching this in my promise.
I have no intention of testing the underlying NeDB library as it has its own tests which execute successfully so this is really all about my own methods.
My coverage thus far:
Here is the unit test solution:
userRepo.js:
import { userTable } from './userTable';
import { User } from './user';
export class UserRepo {
static getBypNick(pNick = '') {
const userSearchRegex = new RegExp(`^${pNick.toLowerCase()}`, 'i');
return new Promise((resolve, reject) => {
userTable
.find({
_pNick: userSearchRegex,
})
.sort({
_pNick: 1,
})
.exec(function(err, result) {
if (err) {
return reject(err);
}
const userArray = result.map((user) => {
return new User(user._pNick, user._firstName, user._userName, user._phoneNumber, user._userID);
});
return resolve(userArray);
});
});
}
}
userTable.js:
const userTable = {
find() {
return this;
},
sort() {
return this;
},
exec(fn) {
console.log('real exec');
fn();
},
};
user.js:
export class User {
constructor(nick, firstName, userName, phoneNumber, userId) {
this.nick = nick;
this.firstName = firstName;
this.userName = userName;
this.phoneNumber = phoneNumber;
this.userId = userId;
}
}
userRepo.test.js:
import { UserRepo } from './userRepo';
import { userTable } from './userTable';
jest.mock('./userTable', () => {
const mUserTable = {
find: jest.fn().mockReturnThis(),
sort: jest.fn().mockReturnThis(),
exec: jest.fn(),
};
return { userTable: mUserTable };
});
describe('47587358', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should get user by nick', async () => {
const mResult = [{ _pNick: '_pNick', _firstName: '_firstName', _userName: '_userName', _phoneNumber: 123456, _userID: 1 }];
userTable.exec.mockImplementationOnce((fn) => {
fn(null, mResult);
});
const actual = await UserRepo.getBypNick('jest');
expect(actual).toEqual(
expect.arrayContaining([
expect.objectContaining({
nick: expect.any(String),
firstName: expect.any(String),
userName: expect.any(String),
phoneNumber: expect.any(Number),
userId: expect.any(Number),
}),
]),
);
expect(userTable.find).toBeCalledWith({ _pNick: new RegExp(`^jest`, 'i') });
expect(userTable.sort).toBeCalledWith({ _pNick: 1 });
expect(userTable.exec).toBeCalledWith(expect.any(Function));
});
it('should handle error', async () => {
const mError = new Error('network');
userTable.exec.mockImplementationOnce((fn) => {
fn(mError, null);
});
await expect(UserRepo.getBypNick('jest')).rejects.toThrowError('network');
expect(userTable.find).toBeCalledWith({ _pNick: new RegExp(`^jest`, 'i') });
expect(userTable.sort).toBeCalledWith({ _pNick: 1 });
expect(userTable.exec).toBeCalledWith(expect.any(Function));
});
});
unit test result with coverage report:
PASS src/stackoverflow/47587358/userRepo.test.js (9.448s)
47587358
✓ should get user by nick (10ms)
✓ should handle error (3ms)
-------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------|----------|----------|----------|----------|-------------------|
All files | 100 | 66.67 | 100 | 100 | |
user.js | 100 | 100 | 100 | 100 | |
userRepo.js | 100 | 66.67 | 100 | 100 | 5 |
-------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.66s
source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47587358

Asserting that method has been called in a promise with Mocha and Sinon

I have the following simplified method and test, where I want to test that handleResponse() has been called.
The test fails with handleResponse() not beeing called at all. If I modify the code to run handleResponse outside the promise, the test completes successfully.
Is this because the promise is async, and the assertion is being run before the promise? If so, how can I wait for the promise to the completed before running the assertion?
Code:
export function fetchList(id) {
// handleResponse(response); // Works here
return (dispatch) => {
service.getList(id).then((response) => {
handleResponse(response); // Doesnt work here
}, (error) => {
addNotification(error);
});
};
}
Test:
describe('fetchList', () => {
let getListStub;
let handleResponseStub;
beforeEach(() => {
getListStub = sinon.stub(service, 'getList');
handleResponseStub = sinon.stub(appActions, 'handleResponse');
});
afterEach(() => {
getListStub.restore();
handleResponseStub.restore();
});
it('should dispatch handleResponse on success', () => {
const dispatchStub = sinon.stub();
const id = 1;
const returnValue = 'return value';
getListStub.withArgs(id).resolves(returnValue);
listActions.fetchList(id)(dispatchStub);
sinon.assert.calledOnce(getListStub);
sinon.assert.calledOnce(handleResponseStub); // Fails
});
});
You forget add async/await to your unit test and change to return service.getList(...). Otherwise, the assertion will execute before resolving the promise of service.getList(...).
listActions.ts:
import * as service from "./service";
import { handleResponse, addNotification } from "./appActions";
export function fetchList(id) {
return dispatch => {
return service.getList(id).then(
response => {
handleResponse(response);
},
error => {
addNotification(error);
}
);
};
}
appActions.ts:
export function handleResponse(res) {
console.log("handleResponse");
}
export function addNotification(error) {
console.log("addNotification");
}
service.ts:
export function getList(id) {
return new Promise(resolve => {
setTimeout(() => {
resolve([]);
}, 5000);
});
}
listActions.spec.ts:
import * as service from "./service";
import * as appActions from "./appActions";
import * as listActions from "./listActions";
import sinon from "sinon";
describe("fetchList", () => {
let getListStub;
let handleResponseStub;
beforeEach(() => {
getListStub = sinon.stub(service, "getList");
handleResponseStub = sinon.stub(appActions, "handleResponse");
});
afterEach(() => {
getListStub.restore();
handleResponseStub.restore();
});
it("should dispatch handleResponse on success", async () => {
const dispatchStub = sinon.stub();
const id = 1;
const returnValue = "return value";
getListStub.withArgs(id).resolves(returnValue);
await listActions.fetchList(id)(dispatchStub);
sinon.assert.calledOnce(getListStub);
sinon.assert.notCalled(dispatchStub);
sinon.assert.calledOnce(handleResponseStub); // Fails
});
});
Unit test result with coverage report:
fetchList
✓ should dispatch handleResponse on success
1 passing (95ms)
---------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------|----------|----------|----------|----------|-------------------|
All files | 83.33 | 100 | 53.85 | 82.86 | |
appActions.ts | 50 | 100 | 0 | 50 | 2,6 |
listActions.spec.ts | 100 | 100 | 100 | 100 | |
listActions.ts | 85.71 | 100 | 75 | 85.71 | 11 |
service.ts | 25 | 100 | 0 | 25 | 2,3,4 |
---------------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/43186634

How to test `catch` and `then` with sinon js?

I have been trying to stub and mock my function to be able to test my function
SetExample.createCandyBox = function(config) {
this.getCandies(config.candyUrl)
.catch(() => {
return {};
})
.then((candies) => {
Advertisements.pushCandyBox(config, candies);
});
};
I want to test a scenario when the config.candyUrl is incorrect(404, etc) with something like:
it('should return to an empty array when url is incorrect', sinon.test(function() {
// Fixture is an element I created for testing.
var configStub = SetExample.getCandyConfig(fixture);
var createCandyBoxMock = sinon.mock(config);
createCandyBoxMock.expects('catch');
SetExample. createCandyBox(configStub);
}));
When I do this, term is error => Can't find variable: config.
What did I do wrong? can someone help and explain? I am new to Sinon :( Thx in advance!
You can use sinon.stub() to stub this.getCandies and its resolved/rejected value. And stub Advertisements.pushCandyBox, then make an assertion to check if it has been called or not.
E.g.
SetExample.js:
const Advertisements = require('./Advertisements');
function SetExample() {}
SetExample.getCandies = async function (url) {
return 'your real getCandies implementation';
};
SetExample.createCandyBox = function (config) {
return this.getCandies(config.candyUrl)
.catch(() => {
return {};
})
.then((candies) => {
Advertisements.pushCandyBox(config, candies);
});
};
module.exports = SetExample;
Advertisements.js:
function Advertisements() {}
Advertisements.pushCandyBox = function (config, candies) {
return 'your real pushCandyBox implementation';
};
module.exports = Advertisements;
SetExample.test.js
const SetExample = require('./SetExample');
const Advertisements = require('./Advertisements');
const sinon = require('sinon');
describe('38846337', () => {
describe('#createCandyBox', () => {
afterEach(() => {
sinon.restore();
});
it('should handle error', async () => {
const err = new Error('timeout');
sinon.stub(SetExample, 'getCandies').withArgs('wrong url').rejects(err);
sinon.stub(Advertisements, 'pushCandyBox');
await SetExample.createCandyBox({ candyUrl: 'wrong url' });
sinon.assert.calledWithExactly(SetExample.getCandies, 'wrong url');
sinon.assert.calledWithExactly(Advertisements.pushCandyBox, { candyUrl: 'wrong url' }, {});
});
});
});
unit test result:
38846337
#createCandyBox
✓ should handle error
1 passing (7ms)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 81.82 | 100 | 42.86 | 81.82 |
Advertisements.js | 66.67 | 100 | 0 | 66.67 | 4
SetExample.js | 87.5 | 100 | 60 | 87.5 | 6
-------------------|---------|----------|---------|---------|-------------------

Categories

Resources