TOLULOPE ADETULA
TOLULOPE ADETULA

Reputation: 788

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.

Upvotes: 1

Views: 889

Answers (1)

Lin Du
Lin Du

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

Related Questions