JS: Продвинутое тестирование

Теория: Тестирование ошибок

Основные тесты, которые нужно писать, это тесты на успешные сценарии работы. Но в некоторых ситуациях код должен возвращать ошибки и их тоже бывает нужно проверять. Под ошибками понимаются ситуации, в которых код выбрасывает исключение. В чем их особенность? Посмотрите на тест:

test('boom!', () => {
  try {
    functionWithException(0)
  }
  catch (e) {
    expect(e).not.toBeNull()
  }
})

Этот код пытается протестировать ситуацию, при которой функция functionWithException() выбрасывает исключение, если ей передать 0. Как вы думаете, этот тест проверит, что функция действительно порождает исключение?

Правильный ответ — нет. Если функция functionWithException() не выбросит исключение, то тест пройдет, так как код не попадет в блок catch.

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

test('boom!', () => {
  // Количество утверждений, которые должны быть запущены в этом тесте
  expect.assertions(1)

  try {
    functionWithException(0)
  }
  catch (e) {
    expect(e).not.toBeNull()
  }
})

Этот способ крайне опасен. Он порождает хрупкие тесты, которые завязаны на то, как они написаны. Если вы захотите добавить новое утверждение, то тест провалится и придется его править. Вам всегда придется следить за тем, чтобы это число было правильным. Не используйте этот подход, чем больше контекстной зависимости, тем сложнее разобраться в коде и проще наделать ошибок.

И наконец-то мы подобрались к правильному способу. В Jest есть матчер, который самостоятельно отлавливает исключение и проверяет, что оно вообще было сгенерировано.

test('boom!', () => {
  expect(() => {
    functionWithException(0)
  }).toThrow()
})

Главная особенность этого матчера в том, что он принимает на вход функцию, которая вызывается внутри. Благодаря этому, он может самостоятельно отследить появление исключения. Этот код не содержит неявного состояния и лишних проверок, он делает ровно то, что нужно делать и не требует от нас слишком много. Более того, теоретически возможен тест, в котором делается сразу несколько проверок на различные исключения. Это значительно сложнее провернуть с предыдущими способами.

Иногда важно не просто поймать исключение, но и убедиться в том, что это ожидаемое исключение. Сделать это можно, передав в матчер toThrow() строку, которая должна присутствовать в сообщении исключения.

test('boom!', () => {
  expect(() => {
    functionWithException(0)
  }).toThrow('divide by zero')
})

Рекомендуемые программы