András Geiszl
András Geiszl

Reputation: 1064

Test overwriting console.log with Jest

I'm filtering some messages with the following code:

const consoleLog = console.log;
console.log = (...args) => {
  if (args.length === 0 || typeof args[0] !== 'string' || !args[0].includes('[HMR]')) {
    consoleLog.apply(console, args);
  }
};

How can I test the console output of this function? Normally, I'd just mock console.log, but in this case it's overwritten by the code above, so I can't do that.

Upvotes: 2

Views: 1179

Answers (2)

Will
Will

Reputation: 7017

Or don't mess with the original console.log at all.

const consoleLog = (...args) => {
  if (args.length === 0 || typeof args[0] !== 'string' || !args[0].includes('[HMR]')) {
    console.log.apply(console, args);
  }
};

Then utilize consoleLog exclusively in your application code.

Benefit of this indirection:

  • It's safer. You never monkey-patch the original console.log at all.
  • It's more future-proof. If you switch from console.log to e.g. a 3rd-party logging framework later, you don't have to adjust all your logging code.

However, if you are intentionally configuring code beyond your control (3rd-party) that uses console.log, this of course will not work for that case.

With this approach, for unit testing, spy on console.log (which was never modified) as recommended in the answer from estus.

Upvotes: 1

Estus Flask
Estus Flask

Reputation: 222319

It's generally not a good practice to monkey-patch global APIs. And it's better to expose original function just in case, at least for testing purposes.

It would be easier to test it if it were more flexible, e.g. patched code could be controlled with environment variables, in case there's a need to fall back to original behaviour:

console._logOriginal = console.log;
console.log = (...args) => {
  if (
    !process.env.NO_HMR_SPAM ||
    (args.length === 0 || typeof args[0] !== 'string' || !args[0].includes('[HMR]'))
  ) {
    console._logOriginal(...args);
  }
};

In this case console._logOriginal can be spied.

Otherwise original console.log should be backed up before this module is evaluated, it shouldn't be imported before that:

const consoleLogOriginal = console.log;

it('...', async () => {
  const consoleLogSpy = jest.spyOn(console, 'log');
  await import('moduleThatManglesConsole');
  console.log('[HMR]');
  expect(consoleLogSpy).not.toHaveBeenCalled();
  console.log('');
  expect(consoleLogSpy).toHaveBeenCalledWith('');
})

afterEach(() => {
  console.log = consoleLogOriginal;
});

Upvotes: 0

Related Questions