#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()
?
Ответ №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()
?