Search code examples
typescriptjestjsfsts-jest

Mocking fs with jest clashes with config module


I'm trying to unit test the following file:

import { readFileSync } from 'fs';
import logger from './logger.utils';

export const readFile = async (targetDir: string, fileName: string): Promise<string> => {
  logger.info(`start reading from ${targetDir}/${fileName}`);
  const data = readFileSync(`${targetDir}/${fileName}`, {encoding: 'utf8', flag: 'r'});

  return data;
};

jest test file

import { mocked } from 'ts-jest/utils';
import fs from 'fs';
jest.mock('fs');
import * as fsUtils from '../../src/utils/fs.utils';

let readFileSyncMock: jest.SpyInstance;

describe('fs.utils', () => {
  describe('readFile', () => {
    afterEach(() => {
      readFileSyncMock.mockReset();
    });
    afterAll(() => {
      readFileSyncMock.mockRestore();
    });
    it('should create a new log directory if one doesn\'t already exist', async () => {
      mocked(fs.readFileSync as jest.Mock).mockImplementation(() => { ('some string') });
    
      const fileData = await fsUtils.readFile('target_directory', 'file_name');
    
      expect(fileData).toEqual('some string');
    });
  });
});

When I run the test I get the following error:

Config file /Users/dfaizulaev/Documents/projectname/config/runtime.json cannot be read. Error code is: undefined. Error message is: Cannot read property 'replace' of undefined

  1 | import loggingContext from './loggingContext';
> 2 | import config from 'config';
    | ^
  3 | import os from 'os';
  4 | import constants from '../../config/constants';
  5 |

  at Config.Object.<anonymous>.util.parseFile (node_modules/config/lib/config.js:821:13)
  at Config.Object.<anonymous>.util.loadFileConfigs (node_modules/config/lib/config.js:697:26)
  at new Config (node_modules/config/lib/config.js:116:27)
  at Object.<anonymous> (node_modules/config/lib/config.js:1492:31)
  at Object.<anonymous> (src/utils/logger.utils.ts:3:1)

The error comes from the logger file imported by the fs module file described above.

logger.utils file

import loggingContext from './loggingContext';
import config from 'config';
import os from 'os';
import constants from '../../config/constants';

const LOG_LEVEL: string = config.get(constants.LOG_LEVEL);

.... more logic ....

I believe that the cause for the error is that I don't properly mock the fs module, but I tried various ways and keep getting this error.

Please advise.


Solution

  • Issue was because I was mocking the entire fs module which is used in config package.

    Removed jest.mock('fs'); and mocked the methods separately.

    For example:

    import fs, { Stats, promises } from 'fs';
    import * as fsUtils from '../../src/utils/fs.utils';
    
    describe('fs.utils', () => {
      afterEach(() => {
        jest.resetAllMocks();
      });
      describe('statSync', () => {
        it('Should successfully call statSync', () => {
          jest.spyOn(fs, 'statSync').mockReturnValue({ size: 1000 } as Stats);
          const result = fsUtils.getFileSize('file path');
          expect(result).toEqual(1000);
        });
      });
      describe('writeFile', () => {
        it('Should successfully call writeFile', async () => {
          jest.spyOn(promises, 'writeFile').mockResolvedValue();
          await fsUtils.writeFile('file path', 'data');
        });
      });
      describe('writeFile', () => {
        it('Should successfully call writeFile', async () => {
          jest.spyOn(promises, 'readFile').mockResolvedValue('some file content');
          const result = await fsUtils.readFile('filePath', 'fileName');
          expect(result).toEqual('some file content');
        });
      });
    });