Lukas
Lukas

Reputation: 10360

Make sure a method to a function has been await'ed for in Jest

I have a function like this (the code is simplified to make this code more readable)

import Adapter from "adapter-package";

const adapter = new Adapter();

async powerOn(): Promise<MyClass> {
  const myClass = new MyClass();
  await adapter.powerOn();
  return myClass;
}

as you can see I am using await on the call of adapter.powerOn(). Now I am writing unit tests for this using Jest.

it("can power on the adapter", async () => {
  const spy = jest.spyOn(Adapter.prototype, "powerOn");
  const myClass = await MyClass.powerOn();
  expect(myClass).toBeInstanceOf(MyClass);
  expect(spy).toHaveBeenAwaitedFor();
              ^^^^^^^^^^^^^^^^^^^^
  spy.mockRestore();
}, 10000);

The test that I have underlined does not exist, but it is what I would like to test. Can I know if a method I have called has been waited for?

edit:

Will Jenkins pointed out that it is not clear what I am asking. Basically I want to make sure that the Promise returned by adapter.powerOn() has been resolved by the time my function has been resolved. Because I already had an issue where I accidentially had removed the await before adapter.powerOn().

async powerOn(): Promise { const myClass = new MyClass(); adapter.powerOn(); return myClass; }

So when I called my function with

await powerOn();

that function was resolved, but it did not await the call to adapter.powerOn() and I had to spend some time debugging. So I would like to make sure that adapter.powerOn() is resolved by the time await powerOn() completes.

Upvotes: 1

Views: 838

Answers (3)

Lukas
Lukas

Reputation: 10360

This is the solution I found:

import { inspect } from "util";

it("waits for the adapter to have been powered on", async () => {
  const spy = jest
    .spyOn(Adapter.prototype, "powerOn")
    .mockImplementation(() => new Promise<void>(res => setTimeout(res, 0)));
  await Sblendid.powerOn();
  const spyPromise = spy.mock.results[0].value;
  expect(inspect(spyPromise)).toBe("Promise { undefined }");
}, 10000);

the line

spy.mock.results[0].value

will get me the promise returned by my mock. Using util.inspect on a promise

expect(inspect(spyPromise)).toBe("Promise { undefined }");

will give me "Promise { undefined }" on a fulfilled promise (that is returning undefined) and would return "Promise { <pending> }" if it weren't fulfilled, that is how I test it has been resolved (fulfilled) when await Sblendid.powerOn(); completed.

Upvotes: 0

Will Jenkins
Will Jenkins

Reputation: 9787

How about something like this?

it("can power on the adapter", async () => {
  let hasResolved = false;
  const spy = jest.spyOn(Adapter.prototype, "powerOn");

  const myClass = await MyClass.powerOn();
  expect(myClass).toBeInstanceOf(MyClass);

  let hasResolved = false;
  //if it has been awaited then this should complete
  spy.returnValues[0].then(()=>{
      hasResolved=true;
  })
  expect(hasResolved).to.be.true;
  spy.mockRestore();
}, 10000)

Upvotes: 1

Daniel Mar&#237;n
Daniel Mar&#237;n

Reputation: 1447

You could use setInmediate to wait for all pending promises:

const flushPromises = () => new Promise(setImmediate);

it("can power on the adapter", async () => {
  const spy = jest.spyOn(Adapter.prototype, "powerOn");
  await expect(MyClass.powerOn()).resolves.toBeInstanceOf(MyClass);
  await expect(flushPromises()).resolves.toBeTruthy();
}, 10000);

This test first checks if powerOn resolves and returns an instance of MyClass and then checks if every pending promise has been resolved or rejected.

Upvotes: 0

Related Questions