Expecting a Promise *not* to complete, in Jest - TagMerge
3Expecting a Promise *not* to complete, in JestExpecting a Promise *not* to complete, in Jest

Expecting a Promise *not* to complete, in Jest

Asked 1 years ago
1
3 answers

I eventually solved this with a custom matcher:

/*
* test-fns/matchers/timesOut.js
*
* Usage:
*   <<
*     expect(prom).timesOut(500);
*   <<
*/
import { expect } from '@jest/globals'

expect.extend({
  async timesOut(prom, ms) {   // (Promise of any, number) => { message: () => string, pass: boolean }

    // Wait for either 'prom' to complete, or a timeout.
    //
    const [resolved,error] = await Promise.race([ prom, timeoutMs(ms) ])
      .then(x => [x])
      .catch(err => [undefined,err] );

    const pass = (resolved === TIMED_OUT);

    return pass ? {
      message: () => `expected not to time out in ${ms}ms`,
      pass: true
    } : {
      message: () => `expected to time out in ${ms}ms, but ${ error ? `rejected with ${error}`:`resolved with ${resolved}` }`,
      pass: false
    }
  }
})

const timeoutMs = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); })
  .then( _ => TIMED_OUT);

const TIMED_OUT = Symbol()

source

The good side is, this can be added to any Jest project.

The down side is, one needs to separately mention the delay (and guarantee Jest's time out does not happen before).

Makes the question's code become:

await expect( eventually("projects/1/userInfo/xyz") ).timesOut(300)

Note for Firebase users:

Jest does not exit to OS level if Firestore JS SDK client listeners are still active. You can prevent it by unsubscribing to them in afterAll - but this means keeping track of which listeners are alive and which not. The firebase-jest-testing library does this for you, under the hood. Also, this will eventually ;) get fixed by Firebase.

Source: link

0

For example:
describe('Fetching', () => {
    const filters = {
        startDate: '2015-09-01'
    };
    const api = new TestApiTransport();

    it('should reject if no startdate is given', () => {
        MyService.fetch().catch(e => expect(e).toBeTruthy()); // see rejects/resolves in v20+
    });            

    it('should return expected data', () => {
        MyService.fetch(filters, null, api).then(serviceObjects => {
            expect(serviceObjects).toHaveLength(2);
        }).catch(e => console.log(e));
    });            
});
UPDATE 8 Dec 2021: At some point Jest started supporting async/await. So while other methods noted work, I've taken to simply (for most cases) using something like:
it('should do something', async () => {
    const expected = true; 
    expect(await funcToTest()).toEqual(expected);
});
As with most cases, async/await is much more readable than alternatives. The only case I use resolves or rejects now is for simple cases like:
it('should not throw when doing something', async () => {
    await expect(funcToTest()).resolves.not.toThrow();
});

it('should throw when something is wrong', async () => {
    await expect(funcToTest()).rejects.toThrow();
});
Nowadays you can write it in this way as well: docs
describe('Fetching', () => {
    const filters = {
        startDate: '2015-09-01'
    };
    const api = new TestApiTransport(); 

 it('should reject if no startdate is given', () => {
   expect.assertions(1);
   return expect(MyService.fetch()).rejects.toEqual({
     error: 'Your code message',
   });
 });          


 it('should return expected data', () => {
   expect.assertions(1);
   return expect(MyService.fetch(filters, null, api)).resolves.toEqual(extectedObjectFromApi);
 });            
});
Either return a promise and expect in the resolve or catch
describe('Fetching', () = > {
  const filters = {
    startDate: '2015-09-01'
  };
  const api = new TestApiTransport();
  it('should reject if no startdate is given', () = > {
    return MyService.fetch()
      .catch (e => expect(e).toBeTruthy()); // see rejects/resolves in v20+
  });
  it('should return expected data', () = > {
    return MyService.fetch(filters, null, api)
      .then(serviceObjects => {
        expect(serviceObjects).toHaveLength(2);
      })
  });
});

Source: link

0

// Don't do this!test('the data is peanut butter', () => {  function callback(data) {    expect(data).toBe('peanut butter');  }  fetchData(callback);});
test('the data is peanut butter', done => {  function callback(data) {    try {      expect(data).toBe('peanut butter');      done();    } catch (error) {      done(error);    }  }  fetchData(callback);});
test('the data is peanut butter', () => {  return fetchData().then(data => {    expect(data).toBe('peanut butter');  });});
test('the fetch fails with an error', () => {  expect.assertions(1);  return fetchData().catch(e => expect(e).toMatch('error'));});
test('the data is peanut butter', () => {  return expect(fetchData()).resolves.toBe('peanut butter');});

Source: link

Recent Questions on promise

    Programming Languages