Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing tests for an async function with Jest

I am trying to test the following function:

import * as config from "./config.js";

export const state = {
  recipe: {},
};

export async function loadRecipe(id) {
  let result;
  let data;
  try {
    result = await fetch(`${config.API_URL}/${id}`);
    data = await result.json();
  } catch (e) {
    console.log(e);
  }
  console.log(result.status);
  if (!result.status === 200) {
    console.log("here");
    throw new Error(`${data.message} (${result.status})`);
    console.log("1here");
  }

  const { recipe } = data.data;
  state.recipe = {
    id: recipe.id,
    title: recipe.title,
    publisher: recipe.publisher,
    sourceUrl: recipe.source_url,
    image: recipe.image_url,
    servings: recipe.servings,
    cookingTime: recipe.cooking_time,
    ingredients: recipe.ingredients,
  };
}

Here are the tests I have written. I am using jest-fetch-mock to mock the global fetch function. If I comment-out the second test and run it, I get the expected results. Now I want to test if a bad id is entered. So I created a second test with bad data and am mocking the result from the API:

"use strict()";
import * as model from "../model.js";
import * as apiResponse from "../__fixtures__/apiResponse.js";
import * as recipes from "../__fixtures__/recipes.js";

beforeEach(() => {
  fetch.resetMocks();
});

describe("Request from the api", () => {
  test("Received valid data", async () => {
    fetch.mockResponseOnce(
      JSON.stringify(apiResponse.id_5ed6604591c37cdc054bca85)
    );
    const res = await model.loadRecipe("5ed6604591c37cdc054bca85");
    expect(model.state.recipe).toStrictEqual(
      recipes.recipe_5ed6604591c37cdc054bca85
    );
    expect(fetch).toHaveBeenCalledTimes(1);
  });

  test("Requested an invalid id", () => {
    const body = apiResponse.invalid_5ed6604591c37cdc054bca85zzzzz;
    const init = { status: 400, statusText: "Bad Request" };
    fetch.mockResponseOnce(JSON.stringify(body), init);
    expect(async () => {
      await model.loadRecipe("5ed6604591c37cdc054bca85zzzzz");
    }).toThrowError();
    expect(fetch).toHaveBeenCalledTimes(1);
  });
});

Whenever the second test is run I get the following error from yarn:

 RUNS  src/js/__tests__/model.test.js
node:internal/process/promises:225
          triggerUncaughtException(err, true /* fromPromise */);
          ^

[UnhandledPromiseRejection: 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(). The promise rejected with the reason "TypeError: Cannot destructure property 'recipe' of '((cov_24wkscmv5p(...).s[10]++) , data.data)' as it is undefined.".] {
  code: 'ERR_UNHANDLED_REJECTION'
}
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Please help me understand what is causing the issue.

like image 798
trevor Avatar asked Feb 21 '26 20:02

trevor


2 Answers

Finally! I got it to work. I had to add rejects to catch the error. I got it from this page: https://eloquentcode.com/expect-a-function-to-throw-an-exception-in-jest

  test("Requested an invalid id", () => {
    const body = apiResponse.invalid_5ed6604591c37cdc054bca85zzzzz;
    const init = { status: 400, statusText: "Bad Request" };
    fetch.mockResponseOnce(JSON.stringify(body), init);
    expect(async () => {
      await model.loadRecipe("5ed6604591c37cdc054bca85zzzzz");
    }).rejects.toThrowError();
    expect(fetch).toHaveBeenCalledTimes(1);
  });
like image 184
trevor Avatar answered Feb 23 '26 10:02

trevor


Basically that error is due to your reject block/case. So, when using async await, you could better keep it inside the try catch block to capture the reject case. Consider following snippet for example -

 var prom = function(p_param) {
    return new Promise((resolve,reject) => {
        setTimeout(()=>{
            if(p_param%2 == 0){
                resolve('Data good'); 
            } else {
                reject('Bad Data');
            }
        }, 3000);
    });
}

async function runMain(p_data){
    console.log('Verifying Data - '+p_data);
    try {
        var t = await prom(p_data);   //resolve
        console.log(t);
    } catch(err) {
        console.log(err);    //reject
    }
}

runMain(5);

This snippet would result in 'reject', Output:

Verifying Data - 5  
Bad Data
like image 29
Manna Avatar answered Feb 23 '26 08:02

Manna