Когда я начал писать тесты с использованием 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, он по-прежнему очень удобен в повседневной рутине тестирования. Кроме того, он побуждает разработчиков писать более понятные и удобочитаемые тесты, что, в свою очередь, приводит к написанию более надежного и удобного в обслуживании программного обеспечения.