Eria
Eria

Reputation: 3182

Vue.js & Jest - The right way to wait for a promise to be fully handled (including clause finally) before performing assertion

I have a component which behaviour looks like this one:

@Component
export default class MyComponent extends Vue {
  public result: number = 0;

  public ready: boolean = false;
  
  // for test purpose only. In the real code, this promise is the result of a call to a dependency service.
  public promise!: Promise<void>;

  private myMethod(): void {
    this.promise
      .then(() => {
        this.result = 1;
      })
      .catch(() => {
        this.result = 2;
      })
      .finally(() => {
        this.ready = true;
        // setTimeout(() => {
        //   this.finallyExecuted = true;
        // }, 50);
      });
  }
}

The execution of a method, triggered from a click on the following button, depends from the resolution of a promise.

<button id="myButton" type="button" @click="myMethod()">My button</button>

I want to unit test this behaviour, using Jest.

// variable 'wrapper' contains the result of a shallow mount of the component, using vue-test-utils
// variable 'component' contains 'wrapper.vm', after the component has been shallow mounted

it('should set the result to 1 and be ready', async () => {
  // Given
  component.promise = Promise.resolve();

  // When
  wrapper.find('#myButton').trigger('click');
  await expect(component.promise).resolves.toBeUndefined();

  // Then
  expect(component.result).toBe(1);
  expect(component.ready).toBe(true);
});

it('should set the result to 2 and be ready', async () => {
  // Given
  component.promise = Promise.reject();

  // When
  wrapper.find('#myButton').trigger('click');
  await expect(component.promise).rejects.toBeUndefined();

  // Then
  expect(component.result).toBe(2);
  expect(component.ready).toBe(true);
});

These 2 unit tests fail because Jest does not wait for the finally clause to be executed before performing the last assertion: the assertion on result passes, but ready is seen as false in both cases.

How can I order Jest to wait for the promise to be fully handled, including the finally clause? If possible, without the use of an additional library.

Upvotes: 0

Views: 1510

Answers (1)

tony19
tony19

Reputation: 138246

The promise call inside myMethod is not awaited, so your test is checking for the results of the call before it's actually occurred.

The solution is for myMethod to return the result of the promise call:

public myMethod(promise: Promise<void>) {
  return promise
    .then(() => {
      this.result = 1;
    })
    .catch(() => {
      this.result = 2;
    })
    .finally(() => {
      this.ready = true;
    });
}

Then your tests could await the result:

await component.myMethod(promise);

GitHub demo

Upvotes: 1

Related Questions