简体   繁体   中英

Mock imported function with jest in an await context

We are creating a node app, based on express, to read from a static local file and return the JSON within the file.

Here is our json-file.js with our route method:

const readFilePromise = require('fs-readfile-promise');

module.exports = {
  readJsonFile: async (req, res) => {
    try {
        const filePath = 'somefile.json';
        const file = await readFilePromise(filePath, 'utf8');

        res.send(file);
      } catch(e) {
        res.status(500).json(e);
      }
  },
};

we use a third party module, fs-readfile-promise which basically turns node readFileSync into a promise.

But we struggle to mock implementation of this third party, to be able to produce two tests: one based on simulated read file (promise resolved) and one based on rejection.

Here is our test file:

const { readJsonFile } = require('../routes/json-file');
const readFilePromise = require('fs-readfile-promise');
jest.mock('fs-readfile-promise');

const resJson = jest.fn();
const resStatus = jest.fn();
const resSend = jest.fn();
const res = {
  send: resSend,
  status: resStatus,
  json: resJson,
};
resJson.mockImplementation(() => res);
resStatus.mockImplementation(() => res);
resSend.mockImplementation(() => res);

describe('json routes', () => {
  beforeEach(() => {
    resStatus.mockClear();
    resJson.mockClear();
    resSend.mockClear();
  });

  describe('when there is an error reading file', () => {
    beforeEach(() => {
      readFilePromise.mockImplementation(() => Promise.reject('some error'));
    });

    it('should return given error', () => {
      readJsonFile(null, res);

      expect(readFilePromise).lastCalledWith('somefile.json', 'utf8'); // PASS
      expect(resStatus).lastCalledWith(500); // FAIL : never called
      expect(resSend).lastCalledWith({ "any": "value" }); // FAIL : never called
    });
  });
});

We tried to place readFilePromise.mockImplementation(() => Promise.reject('some error')); at the top, just after the jest.mock() without more success.

The third party code is basically something like:

module.exports = async function fsReadFilePromise(...args) {
  return new Promise(....);
}

How can we mock and replace implementation of the module to return either a Promise.resolve() or Promise.reject() depending on our test setup to make our test case pass within res.send() or res.status() method?

The last 2 assertions never pass because the test doesn't wait for the promise in: const file = await readFilePromise(filePath, 'utf8'); to resolve or reject in this case, therefore res.send or res.status never get called.

To fix it, readJsonFile is async , you should await it in the test:

it('should return given error', async () => {
      await readJsonFile(null, res);
      ...
 })

How can we mock and replace implementation of the module to return either a Promise.resolve() or Promise.reject() depending on our test setup to make our test case pass within res.send() or res.status() method

Exactly how you're doing it:

readFilePromise.mockImplementation(() => Promise.reject('some error'));

or

readFilePromise.mockImplementation(() => Promise.resolve('SomeFileContent!'));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM