Публичный и приватный интерфейс, запросы и команды

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

Здравствуйте!

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

Если спешите, то вот 3 правила тестирования из этого письма:

  1. Тестируйте публичные методы
  2. Проверяйте, что вернул метод-запрос
  3. Проверяйте, что изменил метод-команда

Публичный интерфейс

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

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

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

Поэтому тестируйте только публичный интерфейс. Не тестируйте приватные методы напрямую, проверяйте публичные методы, использующие их.

Тестируйте публичные методы

Методы-запросы

Методы делятся на два типа. Методы-запросы возвращают значение и ничего не меняют: user.persisted?, user.full_name. Чтобы убедиться, что они работают правильно, проверяйте то, что они вернули:

class Album
  def tags
    tag_list.to_s.split(",").map(&:strip)
  end
end

RSpec.describe Album do
  describe "#tags" do
    let(:album) { described_class.new(tag_list: "rap, r&b, pop") }

    it "returns tags as array" do
      expect(album.tags).to match_array(%w(rap r&b pop))
    end
  end
end

Проверяйте, что вернул метод-запрос

Методы-команды

Методы-команды ничего не возвращают, но что-то меняют: user.destroy!, user.make_sys_admin. Проверяйте эффект от вызова таких методов:

class Album
  def release_on(date)
    self.release_date = date
  end
end

RSpec.describe Album do
  describe "#release_on" do
    let(:album) { described_class.new }
    let(:date) { Date.new(2016, 7, 7) }

    it "sets release date" do
      expect { album.release_on(date) }.to change { album.release_date }.to(date)
    end
  end
end

Проверяйте, что изменил метод-команда

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

Конечно, есть еще методы-метисы, возвращающие значение и что-то меняющие: user.update_attributes, user.acquire_lock!. Как с ними быть вы наверняка догадались.

В следующем письме заглянем в ящик с дублерами, моками, стабами и разберёмся как тестировать модуль А, использующий модуль Б.

Домашнее задание

Найдите в текущем рабочем проекте 5 методов-запросов и 5 методов-команд.

Затем отдохните часок и протестируйте вот эти классы. Срок — до 8 часов следующего понедельника. Если нет доступа или что-то не получается, пишите: vasily@polovnyov.ru.

Второй выпуск свёрстан под саундтрек к «Тёмному рыцарю». Оказывается, Бэтмен два фильма подряд ищет детонатор

Отписаться