Search code examples
javascriptecmascript-6fetchjestjses6-promise

JavaScript (ES6) and fetch(): How can I throw an error so that catch triggers? (Testing it with Jest)


I thought I was decent at JavaScript until now. I want to write a helper function for my HTTP requests. I tested it with Jest. The problem is that the catch() part does not get triggered. Let me give you the test first:

it("recognizes when a response's status is not okay", () => {
  fetch.mockResponseOnce(JSON.stringify({ ok: false }));

  expect.assertions(1);

  return getRequestWithoutHeader(fullTestUrl).catch(err => {
    expect(err.ok).toEqual(false);
  });
});

Maybe the test is written wrongly which fails. Anyways here is the helper function that I did write. I tried out different implementations and they all fail the test:

// Implementation one: with throw
export const getRequestWithoutHeader = fullUrlRoute =>
  fetch(fullUrlRoute).then(response =>
    response.json().then(json => {
      if (!response.ok) {
        throw Error(json);
      }
      return json;
    }, error => error)
  );

// Implementation two: with throw new
export const getRequestWithoutHeader = fullUrlRoute =>
  fetch(fullUrlRoute).then(response =>
    response.json().then(json => {
      if (!response.ok) {
        throw new Error(json);
      }
      return json;
    }, error => error)
  );

// Implementation three: With Promise.reject
export const getRequestWithoutHeader = fullUrlRoute =>
  fetch(fullUrlRoute).then(response =>
    response.json().then(json => {
      if (!response.ok) {
        return Promise.reject(json);
      }
      return json;
    }, error => error)
  );

// Implementation four: with new Promise
export const getRequestWithoutHeader = fullUrlRoute =>
  new Promise((resolve, reject) => {
    fetch(fullUrlRoute).then(response =>
      response.json().then(
        json => {
          if (!response.ok) {
            reject(json);
          }
          resolve(json);
        },
        error => reject(error)
      )
    );
  });

None of these work. Some of these would return using a then in the test, but I want the promise to be thrown. I want to trigger a catch.

How do I have to write this helper function?


Solution

  • Here is what I ended up having to do:

    I used jest-fetch-mock for mocking the requests.

    In order for the promise to be correctly rejected, I had to overrite the init argument of the mockResponseOnce function.

    Here is how the test ended up looking:

      it("recognizes when a response's status is not okay", () => {
        fetch.mockResponseOnce(JSON.stringify({ someResponse: "someResponse" }), { status: 403 });
        expect.assertions(1);
    
        return getRequestWithHeader(fullTestUrl, article).catch(err => {
          expect(err.someResponse).toEqual("someResponse");
        });
      });
    

    By setting the status explicitly, it automatically sets ok: false in the response, triggering the function.

    I also applied CertainPerfomance's tips and refactored the function like this:

    export const getRequestWithoutHeader = fullUrlRoute =>
      fetch(fullUrlRoute)
        .then(response => {
          if (!response.ok) {
            return Promise.reject(response);
          }
          return response.json();
        })