11

I am trying to mock a function in a third-party node module, specifically the fs.readFileSync() function, using jest. There are quite a few examples out there but I haven't found one which uses TypeScript. I have a simple, hopefully minimal, example at github. This is probably a simple issue for someone familiar with jest.

Philipp Kyeck
  • 18,402
  • 15
  • 86
  • 123
nicky88
  • 113
  • 1
  • 1
  • 5
  • Yes, Mr Cantarela, typescript is Microsoft's take on javascript. I did start out with FB's Flow, but found that typescript combined with vscode was quite good so it seemed worthwhile pursuing a combination of typescript/jest(js) :). – nicky88 Aug 31 '18 at 21:44

1 Answers1

19

Here are a few different ways of mocking something like fs.readFileSync():

Mock the function

To mock a function use jest.spyOn() in combination with functions like mockImplementation():

import { returnNameInJsonFile } from './index';
import * as fs from 'fs';

describe('index', () => {

  it('returnNameInJsonFile', () => {
    const mock = jest.spyOn(fs, 'readFileSync');  // spy on fs.readFileSync()
    mock.mockImplementation(() => JSON.stringify({ name: 'myname' }));  // replace the implementation

    const name: string = returnNameInJsonFile('test.json');
    expect(name).toBe('myname');

    mock.mockRestore();  // restore fs.readFileSync()
  });

});

Mock the module using a factory

Pass a module factory to jest.mock():

import { returnNameInJsonFile } from './index';

jest.mock('fs', () => {
  const MOCK_FILE_INFO = { 'test.json': JSON.stringify({ name: 'myname' }) };
  return {
    readFileSync: (fpath, opts) => {
      if (fpath in MOCK_FILE_INFO) {
        return MOCK_FILE_INFO[fpath]
      }
      throw 'unexpected fpath'
    }
  }
});

describe('index', () => {
  it('returnNameInJsonFile', () => {
    const name: string = returnNameInJsonFile('test.json');
    expect(name).toBe('myname'); // 1.0.0 is installed and 2.0.0 is available
  });
});

Mock the module automatically

Create a mock for the module.

Jest will use the mock automatically unless it is a core Node module (like fs) in which case calling jest.mock() is required.

__mocks__/fs.ts:

const fs = jest.genMockFromModule('fs');

let mockFiles: object = {};

function __setMockFiles (newMockFiles: object) {
  mockFiles = newMockFiles;
}

function readFileSync(filePath: string) {
  return mockFiles[filePath] || '';
}

// If anyone knows how to avoid the type assertion feel free to edit this answer
(fs as any).__setMockFiles = __setMockFiles;
(fs as any).readFileSync = readFileSync;

module.exports = fs;

index.test.ts:

import { returnNameInJsonFile } from './index';

jest.mock('fs');  // Required since fs is a core Node module

describe('index', () => {

  const MOCK_FILE_INFO = { 'test.json': JSON.stringify({ name: 'myname' }) };

  beforeEach(() => {
    require('fs').__setMockFiles(MOCK_FILE_INFO);
  });

  it('returnNameInJsonFile', () => {
    const name: string = returnNameInJsonFile('test.json');
    expect(name).toBe('myname'); // 1.0.0 is installed and 2.0.0 is available
  });
});
Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • this works for now - thanks! I've updated [the github example](https://github.com/asmallfurrywombat/jest_typescript_mock_test) – nicky88 Sep 01 '18 at 04:29
  • @nicky88 sounds like you wanted to mock the entire module, I updated my answer with a couple of ways of doing that – Brian Adams Sep 02 '18 at 18:35
  • much appreciated, @brian-lives-outdoors .. I suspect it may be of use to others as well. – nicky88 Sep 05 '18 at 12:50
  • @brian-lives-outdoors this solution _should_ work, but I'm getting an error back from Typescript that is frustrating to fix. `Property '__setMockFiles' does not exist on type 'typeof import("fs")'` I guess I'd need to "trick" TS into realizing there is a function on the 'fs' library (when mocked). And optionally (when using require inline): `TypeError: require(...).__setMockFiles is not a function` – DevanB Jun 19 '19 at 17:27