Examining the asynchronous function to cause an error using mocha

I am facing a challenge with testing an async function that is supposed to run for 2000ms before throwing an exception. Despite my efforts using Mocha / chai, the test does not seem to be working as expected.

Here's what I have attempted:

First approach:

expect(publisher.dispatchMessagelt;ExampleResponseMessagegt;(message, {}, 2 * 1000)).to.eventually.throw();

Although this test passes quickly (in 52ms), it fails to capture the exception thrown after 2 seconds. It appears that the promise of the function is not being awaited at all.

Second attempt:

expect(async () => {
      await publisher.dispatchMessagelt;ExampleResponseMessagegt;(message, {}, 2 * 1000);
    }).to.throw();

This time, the test fails with an error message indicating that the function did not throw an error as expected after the specified timeout period.

The desired outcome is for the test to pass when an exception is thrown after 2000ms, which falls within the designated test case timeout of 4000ms.

Additional details:

When employing the following code snippet, the test successfully captures an error being rejected by the promise (which can also be customized to reject with a specific string) within approximately 2002ms:

    try {
      await publisher.dispatchMessagelt;ExampleResponseMessagegt;(message, {}, 2 * 1000);
    } catch (err) {
      expect(err).to.be.an('Error');
    }

Seeking guidance:

Can you advise on the correct approach to test if an async function throws an exception effectively?

Answer №1

.to.throw() is not designed to be used with async functions because they do not throw errors; instead, they return rejected promises.

This issue specifically pertains to chai-as-promised. According to this discussion, using .to.eventually.throw() may not produce the expected results. It assumes that a promise will resolve with a function that throws an error synchronously when called, which may not align with the behavior of dispatchMessage.

To address this, consider modifying the test for dispatchMessage as follows:

expect(publisher.dispatchMessage<ExampleResponseMessage>(message, {}, 2 * 1000))
.to.be.rejected;

Answer №2

Make sure to add the chai plugin chai-as-promised using NPM:

npm i -D chai-as-promised @types/chai-as-promised

In your test file, import and utilize this plugin like so:

import { expect, use } from 'chai';
use(require('chai-as-promised'));

An important aspect is using the keyword await before expect, and you can check for a specific error message with rejectedWith:

await expect(myPromise()).to.be.rejectedWith('my Error message');

Chai allows async/await testing functions. For ensuring promise rejection, consider using rejected or rejectedWith from the chai-as-promise plugin.

As rejected returns a promise, ensure to include an await statement; otherwise, the test case might conclude before the promise gets rejected.

it('should be rejected', async () => {
  await expect(
    publisher.dispatchMessage<ExampleResponseMessage>(message, {}, 2 * 1000)
  ).to.be.rejected;
}

Answer №3

In order to make the other solutions work, it is necessary to utilize chai-as-promised (since they are not compatible with the default chai setup).

For example:

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');

chai.use(chaiAsPromised);

Prior solution (for reference purposes)

The existing answers were ineffective for me (possibly due to an incompatible Chai version), so I implemented a workaround.

Initially, create this utility function:

const shouldReject = async (promise:Promise<any>, errorClass?:Function|Error, message?:string):Promise<void> => {
  let error = null;
  try {
    await promise;
  }
  catch (e) {
    error = e;
  }
  expect(error).to.not.be.null;
  if(errorClass !== undefined)
    error.should.be.an.instanceOf(errorClass);
  if(message !== undefined)
    error.message.should.equal(message);
};

Within your scenario, you would apply it as follows:

it('should be rejected', async () => {
  await shouldReject(
    publisher.dispatchMessage<ExampleResponseMessage>(message, {}, 2 * 1000)
  );
}

Answer №4

A very effective method I have discovered for anticipating rejected promises in the context of chai and mocha is to utilize the unique two-argument form of the then function. By doing this, there is no need for using chai-as-promised.

await someAsyncApiCall().then(
  () => { throw new Error('Anticipated rejection') },
  () => {}
)

When used this way, the then function executes the first lambda if the promise is fulfilled, and the second lambda if it is rejected.

To enhance reusability, you can define your custom function for this purpose.

async function expectReject(promise, message = 'Expected reject') {
  return promise.then(
    () => { throw new Error(message) },
    () => {}
  )
}

Then, within your test case:

it('should reject', async ()=>{
  await expectReject(someAsyncApiCall())
})

Answer №5

Dealing with async errors using chai seems overly complex to me.

Sometimes, a more straightforward approach can be more appealing:

it('should throw', async () => {
  try {
    await myBadAsyncFunction(someParams)
    assert.fail("Should have thrown")
  } catch (err) {
    assert.strictEqual(err.message, "This should be the error message")
  }
}

This method doesn't involve chai and is easy to comprehend.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Formatting Time in Angular 2 Using Typescript

Upon reviewing my date source, I found the following: TimeR ="2017-02-17 19:50:11 UTC"; Within the HTML file, I implemented this code snippet <span class="text-lg">{{TimeR | date: 'hh:mm'}}</span> The current output displays the t ...

We are unable to update the document in AngularFire as the document reference ID could not be retrieved

I am currently working on an update function that is designed to retrieve a specific document based on its unique document uid, which is connected to the authenticated user's uid. In another part of my code, I have a function that successfully fetche ...

Utilize a script in the node package.json file to execute another script while including an additional parameter; for example, incorporating the mocha watcher

Is there a way to reuse a command already defined in a 'script' section of node's package.json? Let's consider the following practical example: Instead of having an additional -w flag on the watch script: "scripts": { "t ...

Is the parent component not triggering the function properly?

Hey there, I'm working with the code snippet below in this component: <app-steps #appSteps [menuSteps]="steps" [currentComponent]="outlet?.component" (currentStepChange)="currentStep = $event"> <div appStep ...

ESLint detected a promise being returned in a function argument where a void return type was expected

I'm encountering a recurring error whenever I run my ESLint script on multiple routers in my server. The specific error message is as follows: error Promise returned in function argument where a void return was expected @typescript-eslint/no-misuse ...

Open new tab for Angular OAuth2 OIDC login process

Currently, I am incorporating the authorization code flow using angular-oauth2-oidc in my Angular application. It is a fairly straightforward process. However, I would like to have the ability for the login flow to open in a new tab when the login button ...

What causes TypeScript to convert a string literal union type to a string when it is assigned in an object literal?

I am a big fan of string literal union types in TypeScript. Recently, I encountered a situation where I expected the union type to be preserved. Let me illustrate with a simple example: let foo = false; const bar = foo ? 'foo' : 'bar' ...

The placeholder within my input moves up and down when switching the input type from password to text

Currently, I am encountering an issue with the styling of a standard input element in React. Specifically, the placeholder text moves up and down by about 2px when viewed on Chrome, while there are no problems on Safari. How can I go about resolving this i ...

Unable to serve static files when using NextJs in conjunction with Storybook

The documentation for Next.js (found here) suggests placing image file paths under the public directory. I have a component that successfully displays an image in my Next.js project, but it doesn't render properly within Storybook. The image file is ...

Tips for addressing the ESLint issue stating that the package should be included in dependencies instead of devDependencies

Struggling to properly lint my React+Typescript project with ESLint. In one of my components, I'm utilizing the useParams hook from react-router. import { useParams } from 'react-router'; However, ESLint is throwing an error on that line: E ...

Resolving issues with Typescript declarations for React Component

Currently utilizing React 16.4.1 and Typescript 2.9.2, I am attempting to use the reaptcha library from here. The library is imported like so: import * as Reaptcha from 'reaptcha'; Since there are no type definitions provided, building results ...

Obtaining the identifier of a generic type parameter in Typescript

Is there a method in TypeScript to retrieve the name of a generic type parameter? Consider the following method: getName<T>(): string { .... implement using some operator or technique } You can use it like this: class MyClass{ } getName< ...

Tips for triggering a click event automatically after a 2-minute delay in ReactJS using TypeScript

I need assistance automating a button's onClick function to execute after a 2-minute delay. The current button invokes the handleEventVideos() function. What is the best way to automatically trigger the button click after 2 minutes? I had tried creat ...

"Array.Find function encounters issues when unable to locate a specific string within the Array

Currently, I am utilizing an array.find function to search for the BreakdownPalletID when the itemScan value matches a SKU in the array. However, if there is no match found, my application throws a 'Cannot read property breakdownPalletID of undefined& ...

Retrieving array-like form data in a TypeScript file

I need assistance with passing form inputs into a typescript file as an array in an ionic application. The form is located in question.page.html <details *ngFor="let product of products;"> <ion-input type="text" [(ngModel ...

Guide to incorporating the useEffect hook within a React Native View

I am trying to use useEffect within a return statement (inside a Text element nested inside multiple View elements), and my understanding is that I need to use "{...}" syntax to indicate that the code written is actual JavaScript. However, when I implement ...

Creating interactive features for a TypeScript interface

I was looking to create a dynamic interface with custom properties, like so: data: dataInterface []; this.data = [ { label: { text: 'something', additionalInfo: 'something' } }, { bar: { text: ' ...

A guide to integrating a component into another component in Angular

Currently, I am encountering an issue with importing a component into another in my Ionic 5.0.0 application. Within my application, I have two separate modules: ChatPageModule and HomePageModule. My objective is to include the Chat template within the Hom ...

What is the best way to transform private variables in ReactJS into TypeScript?

During my conversion of ReactJS code to TypeScript, I encountered a scenario where private variables were being declared in the React code. However, when I converted the .jsx file to .tsx, I started receiving errors like: Property '_element' d ...

How to efficiently fetch Redux state through reducers with an action-based approach

Utilizing Redux actions to manage a list of contacts, I am facing an issue where I am unable to retrieve the actual state contact. Despite setting the contact in the action, all I receive is the contact set within the action itself. Can someone guide me on ...