Автотесты «на пальцах»

  • Что и как тестировать у модуля

  • Как тестировать модуль, использующий другой модуль

Автотесты «на пальцах»

  • Что и как тестировать у модуля

  • Как тестировать модуль, использующий другой модуль

Модули — это функции, классы, модели, контроллеры — кирпичики, из которых построено приложение.

Что тестировать?

У модулей есть публичный интерфейс, через который с ними работают другие модули. Если представить, что модуль — это машина, то её публичный интерфейс — это функции доступные водителю: «ехать прямо», «повернуть налево», «включить радио».

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

Приватные функции постоянно меняются, переименовываются и исчезают, публичные функции меняются редко. Так в Тесле уже нет ни зажигания, ни топливной рампы, а функции «повернуть налево» и «включить радио» на месте.

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

Как тестировать?

Функции можно разделить на два типа: запросы и команды. Запросы возвращают что‑то и ничего не меняют: user.isEditor(), array.find(), spread.isOnScreen(). Команды что‑то меняют и, бывает, что‑то возвращают: user.destroy(), array.splice(), spread.delete().

Чтобы убедиться, что функция‑запрос работает правильно, проверьте то, что она возвращает:

// Проверим функцию, которая из имени автора совета
// выделяет фамилию и возвращает её в родительном падеже
//
​// С помощью describe задают структуру теста: что тестируем и в каких условиях
describe('#genitiveFamilyName', () => {
  describe('with initials', () => {
    // С помощью it описывают ожидания от кода
    it('returns it as is', () => {
      // С помощью expect сверяют ожидания с реальностью:
      // если они не совпадут, тестовый фреймворк покажет ошибку
      expect(genitiveFamilyName('А. Г.')).toEqual('А. Г.')
    })
  })
  ​
  it('returns surname in genitive form', () => {
    expect(genitiveFamilyName('Максим Ильяхов')).toEqual('Ильяхова')
  })
  ​
  it('returns surname in genitive form for female names', () => {
    expect(genitiveFamilyName('Таня Бибикова')).toEqual('Бибиковой')
  })
})

Чтобы убедиться, что функция‑команда работает правильно, проверьте последствия её вызова:

Мы не случайно отбиваем пустыми строками куски кода. Так отделяют фазы теста: настройку, испытание и проверку. Подробнее расскажу о них в следующих советах.
// Проверим функцию, которая модифицирует ДОМ-дерево,
// пронумеровывая и размечая якоря
​
describe('#morph', () => {
  it('marks empty anchors as .is__blank so we can ignore them in navigation', () => {
    const html = `<div class="anchor"></div>`
    const $ = cheerio.load(html)
    ​
    morph($)
    ​
    expect($('.anchor').attr('class')).toContain('is__blank')
  })
  ​
  it('enumerates them', () => {
    const html = `
      <div class="anchor">One</div>
      <div class="anchor">Two</div>
      <div class="anchor">Three</div>
    `
    const $ = cheerio.load(html)
    ​
    morph($)
    ​
    const anchorIds = $('.anchor').get().map(el => el.id)
    ​
    expect(anchorIds).toEqual(['anchor-1', 'anchor-2', 'anchor-3'])
  })
})
Мы не случайно отбиваем пустыми строками куски кода. Так отделяют фазы теста: настройку, испытание и проверку. Подробнее расскажу о них в следующих советах.

Ещё по теме

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

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