Home > Enterprise >  Why is my mock call giving me an "Cannot read property 'then' of undefined"
Why is my mock call giving me an "Cannot read property 'then' of undefined"

Time:02-25

I'm pretty sure this has something to do with the call being async but that's about as far as my brain can get.

The calling function is:

const { messageNodeResponse } = require('../../../app/lib/message-node-response');

jest.mock('../../../app/lib/am-api.js');

describe('message node response', () => {
  it('should check for message node prompt.', async () => {
    const req = {
      session:
      {
        payload: {
          callbacks: [{
            type: 'NameCallback',
            output: [{ name: 'prompt', value: 'resendExpiredOTP' }],
            input: [{ name: 'IDToken1', value: '' }],
          },
          {
            type: 'NameCallback',
            output: [{ name: 'prompt', value: 0 }],
            input: [{ name: 'IDToken2', value: '' }],
          }],
        },
      },
    };
    const res = await messageNodeResponse(req, 'registration');
    expect(res.statusCode).toEqual(200);
  });
});

The function being tested (in message-node-response.js):

const messageNodeResponse = (req, api) => {
  const requestBody = req.session.payload;
  requestBody.callbacks[1].input[0].value = 0;
  const apiCall = assignApi(api);
  return apiCall(req, requestBody)
    .then((result) => {
      logger.debug(result.data);
      req.session.payload = result.data;
    }).catch((err) => {
      throw err;
    });
};

module.exports = { messageNodeResponse };

Running the unit test gives me the following error: "TypeError: Cannot read property 'then' of undefined" referring to the line "return apiCall(req, requestBody)".

As requested, am-api.js:

const axios = require('axios');
const { v1: uuid } = require('uuid');

const post = async (url, data, req) => axios.post(
  url,
  data,
  {
    headers: {
      'Content-Type': 'application/json',
      'Accept-API-Version': 'protocol=1.0,resource=2.1',
      language: req.cookies.lng ? req.cookies.lng : 'en',
      ...(req.session.clientId) && { 'client-id': req.session.clientId },
      ...(req.session.id) && { 'session-id': req.session.id },
      ...((req.headers['correlation-id']) && { 'correlation-id': req.headers['correlation-id'] }) || { 'correlation-id': uuid() },
      ...(req.headers['user-agent']) && { 'User-Agent': req.headers['user-agent'] },
      ...(req.headers['x-forwarded-for']) && { 'X-Forwarded-For': req.headers['x-forwarded-for'] },
    },
  },
);

const registrationApi = async (req, data) => {
  const url = `${process.env.AM_API_URL}${process.env.AM_REALM_PATH}/authenticate?service=${process.env.AM_REGISTRATION_TREE_NAME}&authIndexType=service&authIndexValue=${process.env.AM_REGISTRATION_TREE_NAME}`;
  return post(url, data, req);
};

const authenticationApi = async (req, data) => {
  const url = `${process.env.AM_API_URL}${process.env.AM_REALM_PATH}/authenticate?service=${process.env.AM_AUTHENTICATION_TREE_NAME}&authIndexType=service&authIndexValue=${process.env.AM_AUTHENTICATION_TREE_NAME}`;
  return post(url, data, req);
};

const passwordResetApi = async (req, data) => {
  const url = `${process.env.AM_API_URL}${process.env.AM_REALM_PATH}/authenticate?service=${process.env.AM_PASSWORD_RESET_TREE_NAME}&authIndexType=service&authIndexValue=${process.env.AM_PASSWORD_RESET_TREE_NAME}`;
  return post(url, data, req);
};

const accountRecoveryApi = async (req, data) => {
  const url = `${process.env.AM_API_URL}${process.env.AM_REALM_PATH}/authenticate?service=${process.env.AM_ACCOUNT_RECOVERY_TREE_NAME}&authIndexType=service&authIndexValue=${process.env.AM_ACCOUNT_RECOVERY_TREE_NAME}`;
  return post(url, data, req);
};

module.exports = {
  registrationApi,
  authenticationApi,
  passwordResetApi,
  accountRecoveryApi,
};

CodePudding user response:

the current jest implementation (v27.5) does only use the synchronous function definitions when auto-mocking. furthermore, the default returning value is undefined for the mocked functions.

to define your mocks correctly it's possible to use the second jest.mock() function argument which is the module factory.

jest.mock('../../../app/lib/am-api.js', () => ({
  registrationApi: () => Promise.resolve({ data: 42 }),
  authenticationApi: () => Promise.resolve({ data: 42 }),
  passwordResetApi: () => Promise.resolve({ data: 42 }),
  accountRecoveryApi: () => Promise.resolve({ data: 42 })
}));

the other option for you is to use the mockAxios module without mockinп your api file itself.

  • Related