В предыдущих советах я рассказал о «механике» модульных тестов: что и как тестировать, что не тестировать. Получился набор правил, которых я придерживаюсь:

  1. Тестировать только публичный интерфейс.

  2. Проверять, что вернул запрос.

  3. Проверять последствия вызова команды.

  4. Использовать заглушки вместо внешних запросов.

  5. Проверять факт вызова внешней команды.

Сегодня расскажу о гигиене в тестах: минимальном наборе правил, которых стоит придерживаться, чтобы писать хорошие тесты.

Пишите в едином стиле и используйте линтеры

Тесты — это тоже код. Как и обычный код, тесты без форматирования и структуры тяжело читать и мучительно поддерживать. В каше из проверок и зависимостей легко допустить ошибку: проверить не то или создать неправильную зависимость и получить бесполезный тест, который всегда проходит.

Чтобы минимизировать количество таких ошибок, программисты придумали стили кодирования — стандарты оформления кода. В них описывают, как форматировать, структурировать и организовывать код и комментарии, какую использовать систему именования, какого кода стоит избегать.

См. совет о линтерах

Чтобы за стилем кодирования следили роботы, а не люди, программисты придумали линтеры — программы, автоматически проверяющие код на соответствие выбранному стилю кодирования.

См. совет о линтерах

Для всех популярных фреймворков тестирования есть плагины к линтерам, которые проверяют специфичные для тестов вещи:

К сожалению, не всё можно проверить автоматически: линтер не сможет понять, насколько хорошо код покрыт проверками, нет ли лишних зависимостей, понятны ли человеку описания проверок. Поэтому помимо линтеров советую изучить сборники лучших практик тестирования:

Разделяйте фазы теста

Тесты состоят из трёх фаз: настройки, испытания и проверки. Во время настройки мы создаём нужные зависимости и заглушки, готовим модуль к тестированию. Во время испытания вызываем функции, которые хотим проверить. Во время проверки сверяем то, что получилось, с тем, что должно быть.

Чтобы быстрее находить и воспринимать эти фазы, отделяйте их друг от друга переводом строки:

// Плохо: всё в кучу
it('wipes user data from state', () => {
  const state = { userName: 'Tony Stark', userFirstName: 'Tony', userEmail: 'tony@stark.com' }
  expect(reducer(state, { type: 'SWITCH_TO_INITIAL_SCREEN' })).toEqual(jasmine.objectContaining({
    userName: null,
    userFirstName: null,
    userEmail: null,
  }))
})

// Хорошо: фазы отделены, видно, что и как проверяем
it('wipes user data from state', () => {
  const state = {  // Настройка
    userName: 'Tony Stark',
    userFirstName: 'Tony',
    userEmail: 'tony@stark.com',
  }
        
  const newState = reducer(state, { type: 'SWITCH_TO_INITIAL_SCREEN' }) // Испытание
  
  expect(newState).toEqual(jasmine.objectContaining({ // Проверка
    userName: null,
    userFirstName: null,
    userEmail: null,
  }))
})



Пишите для людей

Тесты читают люди. Чем человечнее, лаконичнее и понятнее они написаны, тем легче людям с ними работать. Используйте описания тестов, понятные человеку, а не машине:

// Плохо: бесполезное описание
it 'works'

// Хорошо
it 'sends welcome email to presentee'


// Плохо: говорим с машинами
it 'sets lock_status to nil'

// Хорошо
it 'unlocks next achievement'


// Плохо: опять говорим с машинами
it 'calls UsersMailer.reset_notification'

// Хорошо
it 'sends password reset instructions'

P. S. Это был совет о веб‑разработке. Хотите знать всё о коде, тестах, фронтенд‑разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.

Веб‑разработка
Отправить
Поделиться
Запинить

Рекомендуем другие советы