Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocha - How to test for unsettled promise?

I am testing a function that returns a promise. I want to assert that, in certain conditions, the returned promise would never settle (doesn't resolve nor reject).

How can I test this with Mocha?


If I run the following:

describe('under certain conditions', function () {
  let promise;
  beforeEach(function () {
    promise = new Promise((resolve, reject) => {});
  });
  it('should hang forever', function () {
    return promise;
  });
});

I get the following error:

Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves
like image 645
dayuloli Avatar asked Jan 28 '23 11:01

dayuloli


2 Answers

robertklep's answer works, but you'd have to wait for 5 seconds before the test completes. For unit tests, 5 seconds is simply too long.

As you've suggested, you can integrate the lolex library into robertklep's solution, to avoid the wait.

(I am also using a Symbol instead of the string 'timeout', in case your promise resolves, by coincidence, also resolves with the string 'timeout')

import { install } from 'lolex';

describe('A promise', function () {
  let clock;
  before(function () { clock = install() });
  after(function () { clock.uninstall() });

  describe('under certain conditions', function () {
    const resolvedIndicator = Symbol('resolvedIndicator');
    const forever = 600000; // Defining 'forever' as 10 minutes
    let promise;
    beforeEach(function () {
      promise = Promise.race([
        new Promise(() => {}), // Hanging promise
        new Promise(resolve => setTimeout(resolve, forever, resolvedIndicator)),
      ]);
    });
    it('should hang forever', function () {
      clock.tick(forever);
      return promise.then((val) => {
        if (val !== resolvedIndicator) {
          throw Error('Promise should not have resolved');
        }
      }, () => {
        throw Error('Promise should not have rejected');
      });
    });
  });
});
like image 91
d4nyll Avatar answered Jan 29 '23 23:01

d4nyll


Let's start by stating that practically speaking, it's not possible to validate that the promise never settles: at some point you have to decide that it has taken too long for the promise to settle, and assume that it will never settle after that point.

Here's a solution that would place that point at 5 seconds:

it('should hang forever', function() {
  // Disable Mocha timeout for this test.
  this.timeout(0);

  // Wait for either a timeout, or the promise-under-test to settle. If the
  // promise that settles first is not the timeout, fail the test.
  return Promise.race([
    new Promise(resolve => setTimeout(resolve, 5000, 'timeout')),
    promise.then(
      () => { throw Error('unexpectedly resolved') },
      () => { throw Error('unexpectedly rejected') }
    )
  ]);
});
like image 27
robertklep Avatar answered Jan 30 '23 00:01

robertklep