Тестирование async-кода

Урок показывает три способа тестировать асинхронный код в Jest и типичную ошибку с ним.

Главное правило: тест должен дождаться завершения асинхронной операции, иначе Jest посчитает его пройденным раньше времени.

Главная ловушка

Если в тесте есть промис, но вы его не дождались, Jest завершит тест до того, как проверка выполнится — и тест ложно «пройдёт». Поэтому асинхронность нужно явно ждать.

Способ 1: async/await

Самый читаемый вариант — пометить тест async и использовать await:

async function fetchUser(id) {
  return { id, name: 'Аня' }; // в реальности — запрос к API
}

test('возвращает пользователя по id', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('Аня');
});

Способ 2: вернуть промис

Если вернуть промис из теста, Jest сам дождётся его. Удобны матчеры resolves и rejects:

test('промис резолвится в имя', () => {
  return expect(fetchUser(1)).resolves.toEqual({ id: 1, name: 'Аня' });
});

test('запрос несуществующего бросает', () => {
  return expect(fetchUser(-1)).rejects.toThrow('Not found');
});

Не забывайте return (или await) — без него промис не дождётся.

Способ 3: колбэк done

Для старого колбэк-API есть параметр done: тест считается завершённым, только когда вы его вызовете:

test('колбэк вызывается с данными', (done) => {
  loadData((err, data) => {
    expect(data).toBe('ok');
    done(); // сообщаем Jest: тест завершён
  });
});

Сегодня async/await предпочтительнее, но done полезен для событийных и колбэк-интерфейсов.

Почему нужно ждать — на чистом JS

Покажем, как await меняет порядок выполнения:

function fetchUser(id) {
  return Promise.resolve({ id, name: 'Аня' });
}

async function run() {
  console.log('1: до await');
  const user = await fetchUser(1);
  console.log('2: получили', user.name);
  console.log('3: проверка пройдена:', user.name === 'Аня');
}

run();
console.log('синхронный код после run()');

Вывод:

1: до await
синхронный код после run()
2: получили Аня
3: проверка пройдена: true

Обратите внимание: строка «синхронный код после run()» печатается до получения данных. Если бы тест не дождался промиса, проверка (строка 3) не успела бы выполниться — именно поэтому в Jest обязательно await/return.

Итог

  • Асинхронный тест обязан дождаться операции, иначе пройдёт ложно.
  • async/await — самый читаемый способ.
  • resolves/rejects проверяют исход промиса; не забывайте return/await.
  • done — для колбэк- и событийного API.
Проверьте себя
1. Что произойдёт, если в тесте не дождаться промиса?
AJest упадёт с синтаксической ошибкой
BТест может ложно пройти, завершившись раньше проверки
CПромис выполнится дважды
DТест зависнет навсегда
2. Для чего нужны матчеры resolves и rejects?
AЧтобы ускорить промисы
BЧтобы проверить, во что разрешается или чем отклоняется промис
CЧтобы заменить async
DЧтобы пропустить асинхронный тест
3. Когда уместен параметр done?
AВсегда вместо await
BДля колбэк- и событийного API, где нет промиса
CТолько для синхронного кода
DДля отключения теста
Поддержать проект