mocking fs readFile gives Cannot spyOn on a primitive value; undefined given

Viewed 3804

the method is simply:

import fs from 'fs';
function getFile(path) {
  return new Promise(function(resolve, reject) {
    fs.readFile(path, 'utf8', function(err, success) {
      if (err) reject(err);
      else resolve(success);
    });
  });
}

it is called from other methods but I need to mock the readFile and I have tried 3 options all giving errors:

attempt one:

  it('should get data', async () => {

    const spy = jest.spyOn(fs,'readFile')
    .........
    });

It stops at the line with the error:

Cannot spyOn on a primitive value; undefined given

attempt two:

  let readFileCallback;

  jest.spyOn(fs, 'readFile').mockImplementation((path, options, callback) => {
    readFileCallback = callback;
  });

throws implementation error because of the options parameter.

Argument of type '(path: any, options: any, callback: any) => void' is not assignable to parameter of type '(path: string | number | Buffer | URL, callback: (err: ErrnoException, data: Buffer) => void) => void'.

if I remove the options parameter, I get the same error:

Cannot spyOn on a primitive value; undefined given

how can I mock the readFile so it returns some text?

1 Answers

jest.spyOn(object, methodName) should work. Your second attempt almost there. You are facing the type issue of TypeScript. To solve this type issue, you can use type-assertions handle this.

E.g.

index.ts:

import fs from 'fs';

function getFile(path): Promise<string> {
  return new Promise(function(resolve, reject) {
    fs.readFile(path, 'utf8', function(err, success) {
      if (err) {
        reject(err);
      } else {
        resolve(success);
      }
    });
  });
}

export { getFile };

index.test.ts:

import fs, { PathLike } from 'fs';
import { getFile } from './';

describe('63748243', () => {
  it('should read file', async () => {
    const readFileSpy = jest.spyOn(fs, 'readFile').mockImplementation(((
      path: PathLike | number,
      options: { encoding?: null; flag?: string } | undefined | null,
      callback: (err: NodeJS.ErrnoException | null, data: string) => void,
    ) => {
      if (typeof options === 'string' && options === 'utf8') {
        callback(null, '123');
      }
    }) as typeof fs.readFile);
    const actual = await getFile('/fake/path');
    expect(actual).toEqual('123');
    expect(readFileSpy).toBeCalledWith('/fake/path', 'utf8', expect.any(Function));
    readFileSpy.mockRestore();
  });
});

unit test result with coverage report:

 PASS  src/stackoverflow/63748243/index.test.ts (8.99s)
  63748243
    ✓ should read file (6ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |    85.71 |       50 |      100 |    85.71 |                   |
 index.ts |    85.71 |       50 |      100 |    85.71 |                 7 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.04s

source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/63748243

Related