Мы все знаем, что должны тестировать наш код, но на самом деле мы этого не делаем. Думаю, будет справедливо сказать, что большинство из нас откладывают это, потому что в девяти случаях из десяти это означает изучение еще одной концепции. В этом руководстве я познакомлю вас с отличной небольшой платформой для простого тестирования кода JavaScript.
Кстати, вы знали, что специалист по Envato Studio может быстро и легко исправить ошибки JavaScript?
Например , ThemeManiac исправит ошибки JavaScript или проблемы совместимости браузера на вашем веб-сайте или в веб-приложении. Исправления могут быть выполнены очень быстро, в зависимости от сложности и доступной информации. Он также может реорганизовать ваши скрипты и создать совершенно новый пользовательский интерфейс. Он выполнил более 1000 рабочих мест в Envato Studio, и 99% клиентов рекомендуют его.
Шаг 0: Понимание BDD
Сегодня мы будем изучать среду тестирования BDD Jasmine . Но сначала мы остановимся здесь, чтобы очень кратко рассказать о BDD и TDD. Если вы не знакомы с этими аббревиатурами, они означают разработку на основе поведения и разработку на основе тестирования . Я нахожусь в процессе изучения того, что каждый из них на практике и чем они отличаются, но вот некоторые из основных отличий:
BDD и TDD… означают разработку на основе поведения и разработку на основе тестирования .
TDD в простейшем виде это просто:
- Напишите свои тесты
- Смотреть их не удастся
- Заставь их пройти
- Refactor
- Повторение
Это довольно легко понять, а?
BDD немного более сложен: как я понимаю это прямо сейчас, я не думаю, что вы или я, как один разработчик, действительно можем практиковать это полностью; это больше командная вещь. Вот несколько практик BDD:
- Установление целей различных заинтересованных сторон, необходимых для реализации видения
- Вовлечение заинтересованных сторон в процесс внедрения путем разработки программного обеспечения извне
- Использование примеров для описания поведения приложения или единиц кода
- Автоматизация этих примеров для обеспечения быстрой обратной связи и регрессионного тестирования
Чтобы узнать больше, вы можете прочитать обширную статью в Википедии (из которой были взяты эти пункты).
Все это говорит о том, что, хотя Jasmine объявляет себя платформой BDD, мы будем использовать ее в стиле TDD. Это не значит, что мы используем это неправильно. Как только мы закончим, вы сможете с легкостью протестировать свой JavaScript … и я ожидаю, что вы сделаете это!
Шаг 1: Изучение синтаксиса
Жасмин берет много подсказок от Rspec.
Если вы вообще знакомы с Rspec, де-факто платформой BDD, вы увидите, что Jasmine берет много подсказок от Rspec. Тесты Жасмин в основном состоят из двух частей: describe
блоки и блоки. Посмотрим, как это работает.
Мы рассмотрим несколько более приближенных к реальной жизни тестов, но сейчас мы сделаем это просто:
description ('оператор сложения JavaScript', function () { it ('добавляет два числа вместе', function () { ожидать (1 + 2). toEqual (3); }); });
Функции description и it
принимают два параметра: текстовую строку и функцию. Большинство тестовых фреймворков стараются читать как можно больше на английском, и вы можете увидеть это с помощью Jasmine. Во-первых, обратите внимание, что строка, переданная для describe
и строка, переданная it
образуют предложение (своего рода): «Оператор сложения JavaScript добавляет два числа вместе». Затем мы продолжим, чтобы показать как.
Внутри этого блока вы можете написать весь код установки, необходимый для вашего теста. Нам не нужно ничего для этого простого примера. Когда вы будете готовы написать реальный тестовый код, вы начнете с функции expect
, передавая все, что вы тестируете. Обратите внимание, как это также формирует предложение: мы «ожидаем, что 1 + 2 будет равно 3».
Но я забегаю вперед. Как я уже сказал, любая ценность, которую вы expect
будет проверена. Метод, который вы вызываете из возвращенного значения expect
, будет определяться тем, какой тест будет запущен. Эта группа методов называется «сопоставителями», и сегодня мы рассмотрим некоторые из них. В этом случае мы используем toEqual
сопоставления toEqual
, которое проверяет, что значение, переданное expect
и значение, переданное toEqual
являются toEqual
и тем же значением.
Я думаю, что вы готовы перейти на следующий уровень, поэтому давайте настроим простой проект с использованием Jasmine.
Шаг 2: Настройка проекта
Жасмин может быть использован сам по себе; или вы можете интегрировать его с проектом Rails. Мы сделаем первое. В то время как Jasmine может работать вне браузера (например, Node, в других местах), мы можем получить действительно хороший маленький шаблон с загрузкой.
Итак, зайдите на отдельную страницу загрузки и получите последнюю версию. Вы должны получить что-то вроде этого:
Вы найдете актуальные файлы Jasmine Framework в папке lib
. Если вы предпочитаете структурировать свои проекты по-другому, пожалуйста, сделайте это; но мы собираемся сохранить это сейчас.
На самом деле в этом шаблоне проекта есть образец кода. «Фактический» JavaScript (код, который мы хотим протестировать) можно найти в подкаталоге src
; мы скоро разместим наш там. Код тестирования — спецификации — находится в папке spec
. SpecHelper.js
не беспокойтесь о файле SpecHelper.js
; мы вернемся к этому.
Этот файл SpecRunner.html
запускает тесты в браузере. Откройте его (и установите флажок «пройдено» в правом верхнем углу), и вы должны увидеть что-то вроде этого:
Это показывает нам, что все тесты для примера проекта проходят. После того, как вы spec/PlayerSpec.js
этот урок, я рекомендую вам открыть файл spec/PlayerSpec.js
и просмотреть этот код. Но сейчас давайте попробуем этот материал для написания теста.
- Создайте
convert.js
в папкеsrc
. - Создайте
convertSpec.js
в папкеspec
, - Скопируйте файл
SpecRunner.html
и переименуйте его вSpecRunner.original.html
. -
Удалите ссылки на файлы примеров проектов в
SpecRunner.html
и добавьте следующие строки:12<script src=»src/convert.js»><>/script><script src=»spec/convertSpec.js»></script>
Теперь мы готовы создать мини-библиотеку, которая будет конвертировать единицы измерения. Начнем с написания тестов для нашей мини-библиотеки.
Шаг 3: Написание тестов
Итак, давайте напишем наши тесты, не так ли?
Описание ("Преобразовать библиотеку", функция () { описать («преобразователь расстояния», функция () { }); описать ("преобразователь объема", функция () { }); });
Начнем с этого; мы тестируем нашу библиотеку Convert
. Вы заметите, что мы здесь describe
утверждения. Это совершенно законно. На самом деле это отличный способ протестировать отдельные функциональные блоки одной и той же кодовой базы. Вместо двух отдельных описательных вызовов для Convert
расстояний и объемных преобразований библиотеки Convert
, у нас может быть более описательный набор таких тестов.
Теперь о реальных тестах. Я повторю звонки по внутреннему describe
для вашего удобства.
описать («преобразователь расстояния», функция () { it ("конвертирует дюймы в сантиметры", function () { ожидайте (конвертировать (12, "в"). в ("см")). в уравнение (30,48); }); it («конвертирует сантиметры в ярды», function () { ожидаем (конвертировать (2000, «см»). в («ярды»)). в равно (21,87); }); });
Вот наши тесты для преобразования расстояний. Здесь важно что-то заметить: мы еще не написали ни капли кода для нашей библиотеки Convert
, поэтому в этих тестах мы делаем больше, чем просто проверяем, работает ли она: мы на самом деле решаем, как она будет использоваться. (и поэтому реализовано). Вот как мы решили сделать наши конверсии:
Преобразовать (<число>, <из строки единиц>). В (<в строку единиц>);
Да, я согласен с тем, как Jasmine реализовал свои тесты, но я думаю, что это хороший формат. Итак, в этих двух тестах я сделал преобразования самостоятельно (хорошо, с помощью калькулятора), чтобы увидеть, какими должны быть результаты наших вызовов. Мы используем сопоставление toEqual
чтобы проверить, прошли ли наши тесты.
Вот тесты объема:
описать ("преобразователь объема", функция () { it («конвертирует литры в галлоны», function () { ожидайте (конвертировать (3, «литры»). в («галлоны»)). в равно (0,79); }); it ("конвертирует галлоны в чашки", function () { ожидаемо (Convert (2, "галлоны"). в ("чашки")). toEqual (32); }); });
И я собираюсь добавить еще два теста в наш вызов describe
верхнего уровня:
it («выдает ошибку при передаче неизвестного единицы измерения», function () { var testFn = function () { Преобразовать (1, «доллар»). В («иены»); } ожидаем (testFn) .toThrow (новая ошибка («не распознано из-единицы»)); }); it («выдает ошибку при передаче неизвестного to-unit», function () { var testFn = function () { Преобразовать (1, «см»). В («фарлонги»); } ожидаем (testFn) .toThrow (новая ошибка («нераспознанный в единицу»)); });
Они проверяют наличие ошибок, которые следует выдавать, когда неизвестные единицы передаются либо в функцию Convert
либо в метод to
. Вы заметите, что я оборачиваю фактическое преобразование в функцию и передаю его expect
функции. Это потому, что мы не можем вызывать функцию как expect
параметр; нам нужно передать ему функцию и позволить ей вызвать саму функцию. Поскольку нам нужно передать параметр в функцию, мы можем сделать это следующим образом.
toThrow
вещь, на которую стоит обратить внимание, это то, что я представляю новый toThrow
сопоставления: toThrow
, который принимает объект ошибки. Скоро мы рассмотрим еще несколько матчей.
Теперь, если вы откроете SpecRunner.html
в браузере, вы получите следующее:
Большой! Наши тесты не проходят. Теперь давайте откроем наш файл convert.js
и поработаем:
function Convert (number, fromUnit) { вар преобразования = { расстояние: { метров: 1, см: 0,01, ноги: 0,3048, дюймы: 0,0254, ярды: 0,9144 }, объем: { литров: 1, галлоны: 3.785411784, чашки: 0,236588236 } }, weenUnit = false, тип, единица измерения; для (введите преобразования) { if (преобразования (тип)) { if ((единица = преобразования [тип] [изUnit])) { междуUnit = число * единица * 1000; } } } возвращение { to: function (toUnit) { if (weenUnit) { для (введите преобразования) { if (conversions.hasOwnProperty (type)) { if ((unit = Conversions [type] [toUnit])) { исправление возврата (междуUnit / (единица * 1000)); } } } выбросить новую ошибку («нераспознанный блок»); } еще { выбросить новую ошибку («нераспознанный из-единицы»); } исправление функции (num) { вернуть parseFloat (num.toFixed (2)); } } }; }
Мы не собираемся обсуждать это, потому что мы изучаем здесь Жасмин. Но вот основные моменты:
- Мы делаем преобразования, сохраняя преобразование в объекте; Конверсионные числа классифицируются по типу (расстояние, объем, добавьте свое). Для каждого поля измерения у нас есть базовое значение (здесь, в метрах или литрах), в которое все конвертируется. Поэтому, когда вы видите
yards: 0.9144
, вы знаете, что это количество ярдов в метре. Затем, чтобы преобразовать ярды в, скажем, сантиметры, мы умножаемyards
на первый параметр (чтобы получить количество метров), а затем делим произведение наcm
, число метров в сантиметре. Таким образом, нам не нужно хранить показатели конверсии для каждой пары значений. Это также позволяет легко добавлять новые значения позже. - В нашем случае мы ожидаем, что переданные единицы будут такими же, как ключи, которые мы используем в «таблице преобразования». Если бы это была настоящая библиотека, мы бы хотели поддерживать несколько форматов, например «in» , ‘inch’ и ‘дюймов’ — и поэтому мы хотели бы добавить некоторую логику, чтобы сопоставить
fromUnit
с правой клавишей. - В конце функции
Convert
мы сохраняем промежуточное значениеbetweenUnit
, которое инициализируется какfalse
. Таким образом, если у нас нетfromUnit
,betweenUnit
будетfalse
входить в методto
, и поэтому будетbetweenUnit
ошибка. - Если у нас нет
toUnit
, будетtoUnit
другая ошибка. В противном случае мы разделим как необходимое и вернем преобразованное значение.
Теперь вернитесь к SpecRunner.html
и перезагрузите страницу. Теперь вы должны увидеть это (после проверки «Показать пройдено»):
Вот и вы! Наши тесты проходят. Если бы мы разрабатывали настоящий проект здесь, мы бы написали тесты для определенной части функциональности, заставили бы их пройти, написали тесты для другой проверки, сделали их проходными и т. Д. Но так как это был простой пример, мы только что сделали это все одним махом.
И теперь, когда вы увидели этот простой пример использования Jasmine, давайте рассмотрим еще несколько функций, которые он предлагает вам.
Шаг 4: Изучение соответствия
До сих пор мы использовали два сопоставителя: toEqual
и toThrow
. Есть, конечно, много других. Вот несколько, которые вы, вероятно, найдете полезными; Вы можете увидеть весь список в вики .
toBeDefined / toBeUndefined
Если вы просто хотите убедиться, что переменная или свойство определены, для этого есть подходящее средство. Есть также один, чтобы подтвердить, что переменная или свойство не undefined
.
это («определено», функция () { var name = "Андрей"; ожидать (имя) .toBeDefined (); }) это ("не определено", function () { имя вар; ожидать (имя) .toBeUndefined (); });
toBeTruthy / toBeFalsy
Если что-то должно быть правдой или ложью, эти сопоставители сделают это.
это ("верно", функция () { ожидать (Lib.isAWeekDay ()) toBeTruthy (). }); это («ложно», function () { ожидать (Lib.finishedQuiz) .toBeFalsy (); });
toBeLessThan / toBeGreaterThan
Для всех, кого ты числишь. Вы знаете, как они работают:
это («меньше 10», function () { ожидать (5) .toBeLessThan (10); }); это («больше 10», function () { ожидать (20) .toBeGreaterThan (10); });
toMatch
Есть какой-нибудь выходной текст, который должен соответствовать регулярному выражению? Матч toMatch
готов и готов.
it («выводит правильный текст», function () { ожидать (cart.total ()) toMatch (/ \ $ \ d * \ d \ d /.). });
содержать
Этот довольно полезен. Он проверяет, содержит ли массив или строка элемент или подстроку.
it («должен содержать апельсины», function () { ожидаемо (["яблоки", "апельсины", "груши"]). toContain ("апельсины"); });
Есть также несколько других совпадений, которые вы можете найти в вики . Но что, если вы хотите совпадения, которого не существует? В самом деле, вы должны иметь возможность делать что угодно с некоторым кодом настройки и средствами сопоставления, которые предлагает Jasmine, но иногда лучше абстрагировать часть этой логики, чтобы сделать тест более читабельным. По счастливой случайности (ну, на самом деле нет), Жасмин позволяет нам создавать наших собственных сопоставителей. Но для этого нам нужно сначала немного научиться чему-то другому.
Шаг 5: Покрытие до и после
Часто при тестировании кодовой базы вам нужно выполнить несколько строк кода настройки для каждого теста в серии. Было бы больно и многословно копировать это при каждом вызове, поэтому у Jasmine есть небольшая удобная функция, которая позволяет нам назначать код для запуска до или после каждого теста. Давайте посмотрим, как это работает:
описать ("MyObject", функция () { var obj = new MyObject (); beforeEach (function () { obj.setState ( "чистые"); }); it («изменяет состояние», function () { obj.setState ( "грязный"); ожидать (obj.getState ()) toEqual ( "грязная"). }) it («добавляет состояния», function () { obj.addState ( "упакованы"); Ожидаем (obj.getState ()). toEqual (["clean", "packaged"]); }) });
В этом надуманном примере вы можете видеть, как перед каждым тестом состояние obj
устанавливается в «clean». Если мы этого не сделали, то изменения, внесенные в объект в предыдущем тесте, сохраняются в следующем тесте по умолчанию. Конечно, мы могли бы сделать нечто подобное с функцией AfterEach
:
описать ("MyObject", функция () { var obj = new MyObject ("чистый"); // устанавливает начальное состояние afterEach (function () { obj.setState ( "чистые"); }); it («изменяет состояние», function () { obj.setState ( "грязный"); ожидать (obj.getState ()) toEqual ( "грязная"). }) it («добавляет состояния», function () { obj.addState ( "упакованы"); Ожидаем (obj.getState ()). toEqual (["clean", "packaged"]); }) });
Здесь мы настраиваем объект для начала, а затем корректируем его после каждого теста. Если вам нужна функция MyObject
чтобы вы могли попробовать этот код, вы можете получить ее здесь, в GitHub .
Шаг 6: Написание пользовательских соответствий
Как мы уже говорили ранее, клиенты, вероятно, будут полезны время от времени. Итак, давайте напишем один. Мы можем добавить сопоставление в вызове BeforeEach
или вызове it
(ну, я думаю, вы могли бы сделать это в вызове AfterEach
, но это не имело бы особого смысла). Вот как вы начинаете:
beforeEach (function () { this.addMatchers ({ }); });
Довольно просто, а? Мы вызываем this.addMatchers
, передавая ему объектный параметр. Каждый ключ в этом объекте станет именем сопоставителя, и связанная с ним функция (значение) будет определять способ его запуска. Допустим, мы хотим создать сопоставление с проверкой, чтобы увидеть, находится ли одно число между двумя другими. Вот что вы написали бы:
beforeEach (function () { this.addMatchers ({ toBeBetween: function (rangeFloor, rangeCeiling) { if (rangeFloor> rangeCeiling) { var temp = rangeFloor; rangeFloor = rangeCeiling; rangeCeiling = temp; } вернуть this.actual> rangeFloor && this.actual <rangeCeiling; } }); });
Мы просто берем два параметра, проверяем, что первый меньше второго, и возвращаем логическое выражение, которое оценивается как true, если наши условия выполнены. Здесь важно отметить, как мы получаем значение, которое было передано expect
функции: this.actual
.
это («между 5 и 30», функция () { ожидать (10) .toBeween (5, 30); }); это («между 30 и 500», функция () { ожидайте (100). toBeBetween (500, 30); });
Это то, что SpecHelper.js
файл SpecHelper.js
; у него есть вызов beforeEach
который добавляет tobePlaying()
. Проверьте это!
Вывод: развлекайся сам!
С Jasmine вы можете сделать гораздо больше: сопоставители функций, шпионы, асинхронные спецификации и многое другое. Я рекомендую вам изучить вики, если вам интересно. Есть также несколько сопутствующих библиотек, которые облегчают тестирование в DOM: Jasmine-jQuery и Jasmine-fixture (зависит от Jasmine-jQuery).
Так что, если вы пока не тестируете свой JavaScript, самое время начать. Как мы уже видели, быстрый и простой синтаксис Jasmine делает тестирование довольно простым. У тебя просто нет причин не делать этого сейчас, не так ли?
Если вы хотите продолжить разработку JavaScript, почему бы не проверить ассортимент элементов JavaScript на Envato Market? Существуют тысячи сценариев, приложений и фрагментов кода, чтобы помочь вам.