node.jstypescriptunit-testingts-jesthapi.js

Cannot find module error while mocking elements in Jest unit testing with TypeScript in Node.js


I'm currently working on integrating Jest unit testing into my Node.js application that's written in TypeScript. However, I'm encountering an error when trying to mock certain elements. Here's the specific error I'm facing:

enter image description here

Here's a simplified version of my test file (appcontroller.test.ts):

import 'reflect-metadata';
import AppController from '../controllers/AppController';
import { ResponseToolkit, Request } from '@hapi/hapi';
import Config from '../config/Config';

jest.mock('../controllers/AppController');
const mockToolkit = {} as ResponseToolkit;

describe('App Routes - V api', () => {
  it('should return version details', async () => {
    const mockedVersion = 'microservice-template';
    const mockedName = '0.0.1';
    const expectedResponse = {
      "data": {
        "version": "microservice-template",
        "name": "0.0.1"
      }
    };
    const mockRequest = {} as Request;
    (Config.get as jest.Mock).mockReturnValueOnce(mockedVersion).mockReturnValueOnce(mockedName);

    const mockController = jest.fn().mockResolvedValue(expectedResponse);
    const mockAppController = new AppController();
    mockAppController.getAppDetailsHandler = mockController;

    const handler = mockAppController.getAppDetailsHandler.bind(mockAppController);
    const response = await handler(mockRequest, mockToolkit);
    expect(mockController).toHaveBeenCalled();
    expect(response).toEqual(expectedResponse);
  });
});

And here's the relevant part of my TypeScript code (appcontroller.ts) :

import Hapi from '@hapi/hapi';
import { injectable } from 'inversify';
import Config from '@app/config/Config';
import BaseController from './BaseController';

@injectable()
export default class AppController extends BaseController {
 
  public async getAppDetailsHandler(request: Hapi.Request, h: Hapi.ResponseToolkit): Promise<Hapi.ResponseObject> {
    try {
      return this.buildResponse(h, {
        version: Config.get('serviceName'),
        name: Config.get('serviceVersion'),
      });
    } catch (error) {
      return this.handleErrors(h, error);
    }
  }
}

Config.ts

import convict from 'convict';
import dotenv from 'dotenv';
export const LOGGER_TYPE_FILE = 'file';
export const LOGGER_TYPE_CONSOLE = 'console';

/** Configure Dotenv to loads environment */
dotenv.config();

const configurationSchema = convict({
  env: {
    doc: 'The application environment.',
    format: ['local', 'dev'],
    default: 'local',
    env: 'NODE_ENV',
  },
  serviceName: {
    doc: 'Name of the service. It needs to be set in the package.json file.',
    format: String,
    default: process.env.npm_package_name ?? 'service_name_not_set',
    env: 'npm_package_name',
  },
  serviceVersion: {
    doc: 'Version of the service. It needs to be set in the package.json file.',
    format: String,
    default: process.env.npm_package_version ?? 'service_version_not_set',
    env: 'npm_package_version',
  },
});

const Config = configurationSchema.validate({ allowed: 'strict' });

export default Config;

I've tried troubleshooting this by different suggestions, one is adding below code in jest.config.js

 moduleNameMapper: {
    "^~(.*)$": "<rootDir>/src/$1"
  }

but I'm still stuck. Can someone please provide guidance on how to resolve this error?

Thanks in advance for any help!


Solution

  • Assuming you have tried the moduleNameMapper as per David's suggestion,

    // jest.config.js
    
    module.exports = {
      // other Jest configuration options...
      
      moduleNameMapper: {
        // Regular expression mapping module names to paths
        '^@/(.*)$': '<rootDir>/src/$1', // Example: Maps "@/components" to "<rootDir>/src/components"
        
        // Specific mappings for individual modules
        '^utils/(.*)$': '<rootDir>/src/utils/$1', // Example: Maps "utils/foo" to "<rootDir>/src/utils/foo"
        
        // Mocking CSS files
        '\\.(css|less|scss)$': 'identity-obj-proxy', // Example: Mock CSS imports using "identity-obj-proxy"
      },
    };  }
    }
    

    What about transform, are you transpiling correctly?

    transform: {
        '^.+\\.tsx?$': 'ts-jest', // Transform TypeScript files using ts-jest
      },
    
      transform: {
        // Use babel-jest to transpile tests with the next/babel preset
        // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
        '^.+\\.(js|jsx|ts|tsx)$': [
          'babel-jest',
          {
            presets: [
              [
                'next/babel',
                {
                  'preset-react': {
                    runtime: 'automatic',
                    importSource: '@emotion/react',
                  },
                },
              ],
            ],
          },
        ],
      },