adanilev
adanilev

Reputation: 3388

How to test if a void async function was successful with jest?

How do you concisely test if a void async function executed successfully with jest? I'm using TypeScript.

// foo.ts
export class Foo {
  public async bar(): Promise<void> {
    await someAsync();
  }
}

How to test that new Foo().bar() does not throw an error?

Upvotes: 42

Views: 35220

Answers (4)

Alex
Alex

Reputation: 5439

The Promise has the advantage that it should not throw at all, rather be resolved or rejected. On the other hand the toBe() assertion expects an argument, even though there is a Promise<void> in TypeScript.

So what if there is no argument passed (or the argument is void) but it is still evaluated. Then the argument is undefined.

  test("Should resolve", async () => {
      await expect(new Foo().bar()).resolves.toBeUndefined();
  });

Testing for not.toThrow() happend to be a false friend for me, because my Foo.bar() did not throw, nor was it resolved either.

Upvotes: 22

Mr Menezes
Mr Menezes

Reputation: 944

This is the most semantic way I've found. It is even just a translation of the test name.

describe("Foo.bar()", () => {
      test("Should not throw", async () => {
        await expect(new Foo().bar()).resolves.not.toThrow();
      });
    });

Upvotes: 82

skyboyer
skyboyer

Reputation: 23705

...alternative is resolves assertion.

describe("Foo.bar()", () => {
  it("should not throw", () => {
      return expect(new Foo().bar()).resolves.toEqual();
  });
});

Here you have to return result since it's a Promise(and make jest wait until it's fulfilled). It's slightly more laconic if you need just verify resolved(or rejected - there is similar prop rejects for checking rejection value).

But in case you need to run several checks after promise-based function is run like

describe("Foo.bar()", () => {
  it("should not throw", () => {
      const promise = new Foo().bar()
      expect(promise).resolves.toEqual();
      expect(someMock).toHaveBeenCalled();
      return promise;
  });
});

you may find option with async/await is more... satisfying?

PS as for you variant

describe("Foo.bar()", () => {
  it("should not throw", async () => {
    expect.assertions(1);

    try {
      await new Foo().bar();
      expect(true).toBeTruthy();
    } catch {
      // should not come here
    }
  });
});

I believe it's not needed to catch an error - so expect.assertions also becomes redundant. why? uncaught exception will fail your test and it's expected no exception so it's fine to fail it. such a structure will be needed if you expect exception and want to check its properties.

Also if test fails option with expect.assertions will notify you just about it's failed while uncaught exception will highlight specific statement(useful if test has several exception-possible statements)

[UPD] also I missed initial point that you need to check if promise is resolved with any result(my version check it resolves with undefined that is not really good move(it does not ruin anything if function starts returning something if it returned nothing before). Meanwhile we should have at least one check in test...

So maybe your approach with stub expect(true) is the same legit here.

But I'd verify twice if you don't want to make more expect in the same test case. is that statement under test really such isolated? or you just explode related checks into separate it()?

Upvotes: 0

adanilev
adanilev

Reputation: 3388

This is the best way that I've found. Hoping there's something more elegant.

describe("Foo.bar()", () => {
  it("should not throw", async () => {
    expect.assertions(1);

    try {
      await new Foo().bar();
      expect(true).toBeTruthy();
    } catch {
      // should not come here
    }
  });
});

Upvotes: 3

Related Questions