Как проверить, была ли функция void async успешной с помощью jest?

#typescript #promise #jestjs #void

#typescript #обещание #jestjs #пустота

Вопрос:

Как вы можете кратко проверить, успешно ли функция void async выполнена с помощью jest? Я использую TypeScript.

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

Как проверить, что new Foo().bar() не выдает ошибку?

Ответ №1:

Это самый семантический способ, который я нашел. Это даже просто перевод названия теста.

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

Комментарии:

1. Лучший ответ на сегодняшний день

2. Для тех, кто, как и я, пропустил это, await очень важно. Если вы опустите это, то тест всегда будет пройден. Хотя у меня нет другого варианта, я бы предпочел вариант, в котором такого рода ошибка синтаксически невозможна.

3. @TobySmith Лучшее решение, которое я нашел, — это линтер, который помечает необработанные обещания (т. Е. отсутствующие await / void), такие как eslint.

4. Будущему читателю: если вы попытаетесь преобразовать .rejects.throw в .resolves.not.toThrow , то будьте очень осторожны; первому нужна функция, а второму — обещание. Итак await expect(() => Promise.reject()).rejects.toThrow(); и await expect(Promise.resolve()).resolves.not.toThrow();

Ответ №2:

Преимущество Promise в том, что оно вообще не должно выдавать, а должно быть разрешено или отклонено. С другой стороны, toBe() утверждение ожидает аргумент, даже если в TypeScript есть Promise<void> .

Ну и что, если аргумент не передан (или аргумент недействителен), но он все еще вычисляется. Тогда аргумент является undefined .

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

Тестирование для not.toThrow() оказалось для меня ложным другом, потому что мой Foo.bar() не выдал ошибку и также не был разрешен.

Комментарии:

1. Вы можете использовать сопоставитель .toBeUndefined() для проверки наличия неопределенных

2. Кажется, это самый актуальный ответ. Мое использование: expect(fs.access(path, mode.R_OK)).resolves.toBeUndefined()

3. объявление. «и это тоже не было разрешено» — в документации четко сказано, Use resolves to unwrap the value of a fulfilled promise ... If the promise is rejected the assertion fails. поэтому, если ваше обещание от bar() не было выполнено, оно должно завершиться неудачей еще до того, как оно подтвердится toThrow() . Обещание имеет три состояния ожидание, выполнено и отклонено. При использовании await обещание либо выполняется, либо отклоняется. Не могли бы вы, пожалуйста, объяснить, как вы в конечном итоге получаете неразрешенное обещание, но не сбойное утверждение, которое использует .resolves.not.toThrow() ?

4.jest «разрешает» документ, mdn Promise doc

Ответ №3:

Это лучший способ, который я нашел. Надеюсь, есть что-то более элегантное.

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

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

Комментарии:

1. Также не требуется try catch ( await достаточно просто), поскольку тест в любом случае завершится неудачей, если обещание не будет выполнено успешно.

Ответ №4:

…альтернативой является resolves утверждение.

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

Здесь вы должны вернуть результат, поскольку это обещание (и заставить jest ждать, пока оно не будет выполнено). Это немного более лаконично, если вам нужно просто подтвердить, разрешена (или отклонена — есть аналогичный реквизит rejects для проверки значения отклонения).

Но в случае, если вам нужно выполнить несколько проверок после запуска функции на основе обещаний, например

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

возможно, вы сочтете вариант с async/await более … удовлетворительным?

PS что касается вашего варианта

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

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

Я считаю, что это не обязательно для обнаружения ошибки — поэтому expect.assertions также становится избыточным. почему? неперехваченное исключение приведет к сбою вашего теста, и ожидается, что исключения не будет, поэтому его можно провалить. такая структура понадобится, если вы ожидаете исключения и хотите проверить его свойства.

Также, если тест завершается неудачей, опция с expect.assertions уведомит вас просто о сбое, в то время как неперехваченное исключение выделит конкретное утверждение (полезно, если в тесте есть несколько операторов, допускающих исключения)

[UPD] также я пропустил начальный момент, когда вам нужно проверить, разрешено ли обещание с любым результатом (моя версия проверяет, что оно разрешается с undefined , что на самом деле не очень хороший ход (это ничего не разрушает, если функция начинает возвращать что-то, если раньше она не возвращала ничего). Тем временем у нас должна быть хотя бы одна проверка в test…

Так что, возможно, ваш подход с заглушкой expect(true) здесь такой же законный.

Но я бы проверил дважды, если вы не хотите создавать больше expect в одном и том же тестовом примере. действительно ли тестируемое утверждение настолько изолировано? или вы просто разбиваете связанные проверки на отдельные it() ?