Когда я начал писать тесты с использованием jasmine.js и mocha.js, я понял, что оба они не имеют важных функций RSpec, которые активно используются сообществом Ruby. Я имею в виду ленивые переменные (let и subject) и общие поведения (shared_examples_for и т. Д.).

Итак, я просмотрел все пакеты npm, чтобы найти что-то похожее, но безуспешно. В основном я искал эти функции:

  • ленивое вычисление, чтобы можно было составлять переменные в зависимости от операторов context или describe. Также это ускоряет выполнение тестов, поскольку неиспользуемые переменные даже не инициализируются.
  • возможность доступа к переменной по их имени, так что нет необходимости использовать this и связывать его везде
  • автоматическая очистка переменных после каждого теста
  • возможность использовать переменные внутри before и after обратных вызовов (например, RSpec не позволяет этого, но для тестирования e2e с помощью Protractor.js это очень удобно)
  • способность определять испытуемого, чтобы можно было писать однострочные тесты
  • возможность переопределить родительскую переменную и получить к ней доступ внутри вложенного `description`
  • возможность доступа к переменной внутри общего поведения (т. е. внутри другой функции, которая содержит другие describe-ы)

И наконец! Почти все эти функции нашли свою реализацию в пакете npm bdd-lazy-var, который поддерживает как node.js, так и среду браузера.

Шаг назад

Обычно это то, что я вижу в тестах JavaScript:

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

И угадай что? Мы просто скопировали / вставили настройку (т.е. beforeEach) и изменили нужные нам значения. Итак, чем больше у вас случаев, тем больше вы копируете / вставляете и тем больше изменений вы вносите позже при изменении API объектов или требований. Кроме того, мы не очищаем переменные user и invoice в afterEach, что в конечном итоге может привести к высокому использованию памяти или даже к ложноположительным тестам (т. Е. Тест пройден, но в коде есть ошибки).

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

Ленивая оценка

Предыдущий пример можно переписать с использованием ленивых переменных, предоставляемых пакетом bdd-lazy-var:

Поскольку переменные вычисляются лениво (и наследуются от родительского describe), можно переопределить user в дочернем describe. Итак, когда создается $invoice, переменная $user внутри своего определения ссылается на дочернее определение user. Это означает, что ленивое вычисление вместе с наследованием переменных дает еще одну полезную функцию - композицию.
Примечание: $ имя переменной для входа было добавлено, чтобы избежать конфликтов имен. В любом случае, если вы попытаетесь определить уже существующую переменную, будет сгенерировано исключение.

Автоматическая очистка

Благодаря def (аналог let в RSpec) мы знаем, какие переменные были созданы, поэтому можем автоматически очистить их позже.

Кстати, все переменные создаются на уровне набора (describe, context), поэтому невозможно определить переменную внутри теста (и это не имеет смысла). Также имейте в виду, что к ленивым переменным можно получить доступ только внутри конструкций фреймворка тестирования (before, after, afterEach, beforeEach, it), и доступ за пределами этих функций может привести к неожиданным результатам.

Переменные внутри блоков `before` и` after`

Можно использовать переменные внутри обратных вызовов before и after. Когда вы создаете переменную, например, в блоке before, тогда экземпляр будет совместно использоваться только before-s, и после запуска тестов этот экземпляр будет очищен. Такое же поведение применяется к after блокам. Итак, давайте продемонстрируем это на примере:

Испытуемый

Для удобства чтения существует псевдоним для def(‘subject’, …), называемый subject(…). Давайте посмотрим на пример:

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

Документация создается прямо из кода теста! Разве это не круто? Ваш код - это ваша документация! Просто прочтите эти 2 предложения, имея в виду, что наша тема - статья: «Ожидается, что статус статьи будет равным черновику» или «Ожидается, что статья будет черновиком». Это то, что вы увидите в результате.

К сожалению, эта функция в настоящее время не поддерживается, но, вероятно, должна быть!

Переопределение переменных

Иногда бывает полезно переопределить родительскую переменную внутри дочернего describe. Давайте посмотрим на пример с subject:

Такое же поведение применяется к переменным, созданным функцией def.

Общее поведение

Совместное поведение - еще одна полезная функция, которой не нашлось места ни в jasmine.js, ни в mocha.js.

Представим себе случай, когда есть базовый абстрактный класс Collection и 2 потомка Array и Set. Другими словами, Array и Set имеют одинаковое поведение, которое мы хотим проверить. Это могло быть что-то вроде этого:

Затем вы можете повторно использовать этот набор тестов в конкретных тестах как для Array, так и для Set. Посмотрим, как

Чтобы быть более точным, мы могли бы использовать itBehavesLike вместо includeExamplesFor, базовая реализация которого выглядит так:

Иногда полезно определить несколько дополнительных помощников, которые упростят тесты и уменьшат шаблон, например, itValidates (‘« Имя пользователя »как уникальное») `. Передав дополнительные аргументы в `includeExamplesFor` и` itBehavesLike`, мы можем добавить простую логику к нашим общим примерам.

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

И тест

К сожалению, эта функция также недоступна в библиотеке и, вероятно, должна поставляться в виде отдельного пакета.

Заключение

Несмотря на то, что bdd-lazy-var не реализует все функции RSpec, он по-прежнему очень удобен в повседневной рутине тестирования. Кроме того, он побуждает разработчиков писать более понятные и удобочитаемые тесты, что, в свою очередь, приводит к написанию более надежного и удобного в обслуживании программного обеспечения.