Reputation: 788
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.
Upvotes: 1
Views: 889
Reputation: 102417
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
Upvotes: 1