首页 > 解决方案 > Jest test execution shows UnhandledPromiseRejectionWarning despite the test is working well

问题描述

I will try to describe the problem with the minimal reproduction code.

So I have a function with a dependency like this one:

import "./styles.css";

function thatRecivesInjectedDependency(aDependency) {
  aDependency
    .doSomethingAsync()
    .then((result) => {
      console.log("it happened");
    })
    .catch((error) => {
      throw error;
    });
}

export default thatRecivesInjectedDependency;

And I want to test it with a test double like this:

import sut from ".";

test("should throw an error if the the assets load went wrong", async () => {
  const fakeDependency = {
    doSomethingAsync: jest
      .fn()
      .mockImplementation(() => Promise.reject(new Error("ERROR!")))
  };

  sut(fakeDependency);

  await expect(fakeDependency.doSomethingAsync).rejects.toThrowError(
    new Error("ERROR!")
  );
});

For some reason, is the only way I found to make the test work, it's not giving me false positive results, you can check it by changing the error text in the mockImplementation or in the toThrowError so it will fail, but still is showing up:

(node:46044) UnhandledPromiseRejectionWarning: Error: ERROR!
(node:46044) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 114)

Any clue that what it's happening and how can I resolve it? Did something wrong?

标签: javascriptunit-testingtestingjestjs

解决方案


This is a no-op:

.catch((error) => {
  throw error;
});

catch can be perceived by a developer as handled error but this one just rethrows it. If the intention is to allow a caller to handle the error, it can be omitted. If the intention is to suppress the error, it can be changed to catch(() => {}) but this may lead to other mistakes, as a caller cannot determine if there was an error while this may be important.

thatRecivesInjectedDependency doesn't return a promise and there's loose promise chain. A caller is unable to chain it and wait for thatRecivesInjectedDependency to finish. That catch doesn't really handle errors makes it impossible to handle them in a caller.

It should be:

  return aDependency
    .doSomethingAsync()
    .then((result) => {
      console.log("it happened");
    });

The test doesn't chain the result, this will result in unhandled rejection. Instead it tests itself because it asserts that mocked function rejects; this is already known because this is exactly that was written several lines above.

The coverage can be increased by asserting console.log.

It should be:

  jest.spyOn(console, 'log');
  await expect(sut(fakeDependency)).rejects.toThrowError("ERROR!");
  expect(console.log).not.toBeCalled();

Notice that toThrowError(new Error("ERROR!")) is excessive because it checks either error constructor or message but not both.


推荐阅读