Не погружайтесь сразу, сначала напишите план, а затем переведите его в код.

Проблема

В программном обеспечении именование часто называют одной из самых сложных частей. Судя по тому, что я видел, написание тестов тоже стоит на первом месте! Я постоянно вижу, как люди борются с тестами. Они перегружены или разочарованы и просто пропускают его, что плохо для всех.

Называть имена сложно, потому что разработчикам редко дают руководство по именованию. Со временем они узнают советы и рекомендации и разрабатывают стратегии, которые упрощают присвоение имен. То же самое можно сказать и о тестировании.

Далее следует стратегия, которую я люблю использовать при написании тестов. Это тот, который я призвал свою команду использовать с большим успехом.

Прелюдия

Во-первых, описанные здесь стратегии работают с большинством языков. Приведенные ниже примеры представлены на Javascript, а в примерах кода предполагается, что Jest является тестовой библиотекой.

Во-вторых, это не TDD. TDD — это совершенно другое животное, и оно выходит за рамки этой статьи.

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

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

Определять

Назвать сложно, потому что вариантов так много. Тесты сложны, потому что люди редко говорят о стратегии, руководящих принципах или даже о том, с чего начать. Конечно, там есть информация, но мне было трудно новичкам понять и применить ее на практике. Часто их пугает внешний вид синтаксиса, и если это их не пугает, они застревают на том, что писать для блоков describe или test.

Я добился успеха, сначала разбив тесты на предложения. Прежде чем мы напишем одну строку кода, мы сначала запишем, что мы хотим охватить. Мы должны сосредоточиться на логических ветвях и не увязнуть в коде. По крайней мере, пока нет.

Каждая группа тестов должна состоять из трех разделов: topic, state(s) и expected result(s).

Тема: то, что мы тестируем. Это может быть функция, метод класса, компонент React и т. д.

Состояния. Это варианты, которые может обрабатывать Topic. Их может быть любое количество, а также могут быть расширения States.

Ожидаемый результат. Учитывая конкретные входные данные, мы должны ожидать конкретного результата.

Начните с предложений

Я рекомендую моей команде начинать с предложений, которые чем-то похожи на псевдокод. Это полезно для тех, кому не так удобно писать тесты. Это упражнение позволяет вам сосредоточиться на определении того, что тестировать, прежде чем беспокоиться о том, как написать какой-либо код для выполнения теста.

Давайте посмотрим, как выглядит эта структура.

topic
- when (a specific state)
  - should (the expected result)

Для более сложных состояний мы немного расширяем это, чтобы оно выглядело так

topic
- when (a specific state)
  - and (and extension to this state)
    - should (the expected result)
  - and (and extension to this state)
    - should (the expected result)

Здесь есть несколько ключевых слов, о которых мы еще не говорили. Эти слова важны, и их всегда следует использовать в начале каждого предложения.

Когда: это первый вариант State.
пример: "когда передано отрицательное число", "когда свойство не определено", "когда луна полная" и т. д.

И еще: это расширение родительского State. Дополнительная проблема или логическая ветвь, которая может возникнуть в State.
пример: «и #currentSearchTerm является пустой строкой», «и #currentSearchTerm включает #name» и т. д.

Должен: Ожидание. Что мы ожидаем здесь? Это предложение, вероятно, будет похоже на ваше последнее утверждение.
пример: "не следует выбрасывать" или "должен возвращать экземпляр Foo", "должен возвращать ошибку" и т. д.

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

На мой взгляд, вам действительно не нужно намного больше с точки зрения структуры. Я бы посоветовал вам вкладывать when и and не глубже 3–4 уровней. При этом у вас есть удобочитаемость, контекст и простота. Его легко поддерживать, и есть встроенные ожидания для всех, когда они пишут тесты. Это приводит к одинаковости, которая затем приводит к ремонтопригодности. 🎉

Напишите несколько тестов

Теперь, когда мы прошли теорию, давайте заставим ее работать! Посмотрите на небольшую функцию ниже. Мы будем использовать эту функцию как Topic в некоторых тестах, чтобы увидеть, как все это может работать в реальной жизни.

Здесь у нас есть простая функция с именем max, которая принимает два значения, сравнивает их и возвращает максимальное из двух. В этой функции есть некоторая логика, которую нам нужно проверить. Начнем с предложений, а не кода.

Сначала подумайте о некоторых возможных логических разветвлениях, а затем используйте слова, о которых мы говорили выше, чтобы объяснить их. Начало часто является самой сложной частью, поэтому давайте начнем с малого. Мы даже не будем заботиться о expectations здесь, только о некоторых из states.

О каких логических ответвлениях нам здесь следует позаботиться? Это не должно быть идеальным или даже полным, нам просто нужно с чего-то начать.

max
- when `valueOne` is equal to `valueTwo`
- when `valueOne` is less than `valueTwo`
- when `valueOne` is greater than `valueTwo`

Это кажется слишком простым, верно? Давайте продолжим, добавив некоторые из expectations, которые сопровождают эти состояния.

max
- when `valueOne` is equal to `valueTwo`
  - should return `valueOne`
- when `valueOne` is less than `valueTwo`
  - should return `valueTwo`
- when `valueOne` is greater than `valueTwo`
  - should return `valueOne`

Вот оно! У нас есть несколько предложений, которые мы можем перевести в код. Эти предложения читаются как на английском, а не как псевдокод. Давай, прочитай один из этих блоков про себя прямо сейчас.

Красиво читается не правда ли 😉

Есть ли здесь какие-то условия, которые я пропустил? (Подсказка: да). Можете ли вы найти их и написать свои собственные предложения?

Перевести предложения в тесты

Используя предложения, которые мы уже написали, мы можем теперь преобразовать их во что-то похожее на настоящий тестовый файл:

Хотите верьте, хотите нет, но этого кода достаточно для запуска! Jest выводит разные цвета для test.todo() блоков, так что это действительно правильный код! Лично мне нравится строить свои тесты таким образом.

Однако использование todo не дает нам ничего, кроме списка задач, поэтому давайте заполним их некоторыми реальными тестами:

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

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

  • используйте переменную с именем result для хранения вывода тестируемого элемента
  • когда это имеет смысл, используйте переменную с именем expectedResult для хранения, как вы уже догадались, ожидаемого результата

Благодаря этому ваши тесты читаются действительнохорошо:

expect(result).toEqual(expectedResult)

Это имеет дополнительные преимущества: а) наличие надежного места для изменения ожидания, если код изменится в будущем, и б) поддерживает чистоту утверждения.

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

Перемещение входных данных в переменные дает им контекст. Эти цифры теперь что-то значат. Давая этим вещам имена, можно легко понять, что происходит и, что более важно, каков контекст. Любой может легко посмотреть на эти тесты и понять, что происходит.

Заключение

Вот оно! Сначала напишите тесты с предложениями, а затем переместите их в код. Это делает процесс предсказуемым, менее пугающим и легким для обучения.

topic -> when -> should

Каждый раз, когда я пишу тесты, я начинаю так. Я конкретизирую логические ветви, затем блоки ожиданий, а затем начинаю заполнять тестовый код. Я выполняю то же упражнение, когда планирую новую работу для своей команды, и в некоторых новых случаях включаю эти предложения в настоящие тикеты. Мне не терпится увидеть, как это закончится, следите за обновлениями!

Рекомендации

Дополнительные материалы на plainenglish.io