Advertisement
  1. Code
  2. Web Development
Code

Тестируем JavaScript с Jasmine

by
Difficulty:IntermediateLength:LongLanguages:

Russian (Pусский) translation by Masha Kolesnikova (you can also view the original English article)

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


Шаг 0: Понимание BDD

Сегодня мы собираемся изучить Jasmine - BDD фреймворк для тестирования. Но мы немного остановимся здесь, чтобы поговорить кратко о BDD и TDD.  Раскроем определение каждой из этих аббревиатур для тех, кто ещё не сталкивался с ними: BDD - это разработка, основанная на поведении, а TDD - это разработка через тестирование. В данный момент я ещё занимаюсь изучением всех особенностей каждой из этих технологий на практике, но уже могу выделить некоторые из основных отличий:

BDD и TDD ... означают Разработку, основанную на поведении (функционировании) и Разработку, основанную на тестировании.

TDD в простой форме можно описать следующим образом:

  1. Пишешь тесты
  2. Отслеживаешь причины, по которым тесты не проходят
  3. Добиваешься успешного прохождения тестов
  4. Рефакторишь
  5. Повторяешь

Это довольно просто понять, не так ли?

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

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

Для более близкого ознакомления вы можете прочитать обширную статью на Википедии (откуда были взяты перечисленные здесь пункты).

Подведем итоги ко всему выше сказанному, в то время как Jasmine представляет собой BDD фреймворк, мы собираемся использовать больше как TDD. Это не значит что мы будем использовать его неправильно. После того, как мы закончим обучение, вы с легкостью сможете проверить ваш JavaScript код ... и я рассчитываю, что вы это сделаете!


Шаг 1: Изучаем синтаксис 

Jasmine многое перенял от Rspec.

Если вы знакомы с Rspec, в рамках BDD фреймворка, то вы увидите, что Jasmine многое от него перенял. В Jasmine тестах можно выделить 2 важных части: describe блоки и блоки it. Давайте посмотрим как это работает.

Мы обязательно рассмотрим близкие к реальным ситуациям тесты, но пока разберем более простые:  

Обе функции describe и it имеют 2 параметра: строку и функцию. Большинство тест фреймворков пытаются делать максимально удобными для чтения на английском, Jasmine не стал исключением. Обратите внимание, что строки, переданные в describe и в it формируют следующее предложение :"JavaScript оператор сложения складывает 2 числа вместе". Теперь рассмотрим как это работает.

Внутри блока it вы можете указать все настройки кода, который собираетесь протестировать. С этим вроде все понятно, приводить снова пример не будем.  Когда вы соберетесь протестировать ваш код, вы начнете с того, что передадите его в функцию expect. Обратите внимание на такое предложение в примере:"expect 1+2 to equeal 3".

Но я забегаю немного вперед. Как я уже сказал, любое значение, переданное в expect будет протестировано. Вызываемый метод, который возвращает expect, будет определен запускаемым тестом.  Эта группа методов называется 'вычислителями', и мы рассмотрим некоторые из них сегодня. В этом случае мы используем вычислитель toEqual, который проверяет, что значение, переданное в expect совпадаем со значением переданным в toEqual.

Я думаю, вы готовы перейти на следующий этап, так давайте настроим простой проект используя Jasmine.


Шаг 2: Настройка проекта

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

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

Jasmine Download

Актуальные файлы Jasmine фреймворка вы найдете в парке lib. Если вы предпочитаете структурировать ваши проекты по-другому, то, пожалуйста, мы же собираемся придерживаться пока такой структуре.

В этом шаблоне проекта есть пример кода. «Фактический» JavaScript (код, который мы хотим проверить) можно найти в подкаталоге src; мы скоро поместим наш код. Код с тестами - спецификации - находится в папке spec. Не беспокойтесь о файле SpecHelper.js; мы еще вернемся к нему.

Этот файл SpecRunner.html запускает тесты в браузере. Откройте его (и установите флажок «пройденный» в верхнем правом углу), и вы увидите что-то вроде этого:

Sample Tests

Это показывает нам, что все тесты для нашего проекта проходят. После того, как вы пройдете этот учебник, я рекомендую вам открыть файл spec/PlayerSpec.js и ознакомиться с этим кодом. Но прямо сейчас давайте попробуем эту тестовую запись.

  • Создайте convert.js в папке src.
  • Создайте convertSpec.js в папке spec,
  • Скопируйте файл SpecRunner.html и переименуйте его SpecRunner.original.html.
  • Удалите ссылки на образцы файлов проекта в SpecRunner.html и добавьте следующие строки:

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


Шаг 3: Написание тестов

Итак, давайте напишем наши тесты, не так ли?

Мы начнем с этого; мы тестируем нашу библиотеку Convert. Вы заметите, что здесь мы вставляем describe выражения. Это совершенно законно. На самом деле это отличный способ проверить отдельные фрагменты функциональных возможностей одной и той же базы кода. Вместо двух отдельных describe вызовов для библиотеки Convert и конвертации расстояний, а так же конвертации объемов, у нас может быть более описательный набор тестов, подобных этому.

Теперь фактические тесты. Я буду повторять внутренние describe вызовы здесь для вашего удобства.

Вот наши тесты для преобразования расстояний. Здесь важно заметить что мы еще не написали код для нашей библиотеки Convert, поэтому в этих тестах мы делаем больше, чем просто проверяем, работает ли это: мы фактически решаем, как мы будем пользоваться нашей библиотекой (и, следовательно то, как она должна быть реализована). Вот как мы решили сделать наши конвертации:

Да, я понимаю, как Jasmine выполнил свои тесты, но я думаю, что это хороший формат. Итак, в этих двух тестах я сам сделал конвератции (нормально, с калькулятором), чтобы узнать, каковы будут результаты наших вызовов. Мы используем toEqual matcher, чтобы проверить, проходят ли наши тесты.

Вот тесты для объемов:

И я собираюсь добавить еще два теста в наш вызов describe верхнего уровня:

Они проверяют ошибки, которые должны быть выброшены, когда неизвестные единицы передаются либо в функцию Convert, либо в метод to. Вы заметите, что я обертываю фактическое преобразование в функцию и передаю ее функции expect. Это потому, что мы не можем вызывать функцию в качестве параметра expect; нам нужно передать ему функцию и дать ей вызов самой функции. Поскольку нам нужно передать параметр в функцию to, мы можем сделать это таким образом.

Важно отметить, что я представляю новый matcher: toThrow, который принимает объект ошибки. В ближайшее время мы рассмотрим еще несколько помощников.

Теперь, если вы откроете SpecRunner.html в браузере, вы получите следующее:

Convert failed tests

Отлично! Наши тесты терпят неудачу. Теперь давайте откроем наш файл convert.js и проделаем определенную работу:

Мы не собираемся обсуждать это, потому что здесь мы изучаем Jasmine. Но вот основные моменты:

  • Мы делаем преобразования, сохраняя конвертацию в объекте; конвертации классифицируются по типу (расстояние, объем, добавьте свои собственные). Для каждого поля измерения мы имеем базовое значение (метры или литры, здесь), к которому все преобразовывается. Поэтому, когда вы видите yards: 0.9144, вы знаете, что это количество ярдов в метрах. Затем, чтобы преобразовать ярды, скажем, в сантиметры, мы умножаем yards на первый параметр (чтобы получить количество метров), а затем разделим произведение на cm, количество метров в сантиметре. Таким образом, нам не нужно сохранять коэффициенты конверсии для каждой пары значений. Это также упрощает добавление новых значений позже.
  • В нашем случае мы ожидаем, что переданные единицы будут такими же, как ключи, которые мы используем в таблице преобразования. Если бы это была настоящая библиотека, мы бы хотели поддерживать несколько форматов - « in», « inch» и « inches» - и поэтому мы хотели бы добавить некоторую логику, чтобы соответствовать fromUnit.
  • В конце функции Convert мы сохраняем промежуточное значение в betweenUnit, которое инициализируется значением false. Таким образом, если у нас не будет fromUnitbetweenUnit будет false в методе to, и, следовательно будет выброшена ошибка.
  • Если у нас нет toUnit, будет выброшена другая ошибка. В противном случае мы разделим по мере необходимости и вернем преобразованное значение.

Теперь вернитесь к SpecRunner.html и перезагрузите страницу. Вы должны увидеть это (после установки флажка «Показать успешные»):

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

И теперь, когда вы видели этот простой пример использования Jasmine, давайте рассмотрим еще несколько функций, которые он нам предлагает.


Шаг 4: Изучение матчеров

До сих пор мы использовали два матчеры: toEqual и toThrow. Есть, конечно, и многие другие. Вот некоторые из них, которые вы, вероятно, найдете полезными; вы можете увидеть весь список в вики.

toBeDefined / toBeUndefined

Если вы просто хотите убедиться, что переменная или свойство определено, для этого есть совпадение. Также можно подтвердить, что переменная или свойство undefined.

toBeTruthy / toBeFalsy

Если что-то должно быть истинным или ложным, эти помощники сделают это.

toBeLessThan / toBeGreaterThan

Для всех математиков. Вы знаете, как они работают:

toMatch

Есть ли какой-нибудь выходной текст, который должен соответствовать регулярному выражению? Матчер toMatch готов для этого.

toContain

Этот очень полезен. Он проверяет, содержит ли массив или строка элемент или подстроку.

Есть еще несколько помощников, которые вы можете найти в вики. Но что, если вы хотите свой матчер? На самом деле, вы должны иметь возможность делать что угодно с некоторым кодом настройки и матчерами Jasmine, но иногда лучше отвлечь часть этой логики, чтобы получить более читаемый тест. Серьезно (ну, на самом деле нет), Jasmine позволяет нам создавать свои собственные матчи. Но для этого нам сначала нужно кое-что узнать.

Шаг 5: Выполнение до и после

Часто - при тестировании базы кода - вы хотите выполнить несколько строк кода настройки для каждого теста в серии. Было бы больно и многословно копировать это для каждого it вызова, поэтому у Jasmine есть удобная небольшая функция, которая позволяет нам назначать код для запуска до или после каждого теста. Давайте посмотрим, как это работает:

В этом надуманном примере вы можете видеть, как до запуска каждого теста состояние obj установлено в «clean». Если мы этого не сделали, изменения, внесенные в объект в предыдущем тесте, сохраняются до следующего теста по умолчанию. Конечно, мы могли бы сделать что-то подобное с функцией AfterEach:

Здесь мы создаем объект для начала, а затем исправляем его после каждого теста. Если вы хотите функцию MyObject, чтобы попробовать этот код, вы можете получить его здесь, на GitHub.

Шаг 6: Создание пользовательских матчеров

Как мы уже говорили ранее, клиентские помощники, вероятно, иногда могут быть полезны. Так что давайте напишем один. Мы можем добавить матчер либо к вызову BeforeEach, либо к вызову it (ну, я думаю, вы могли бы сделать это в вызове AfterEach, но это не имело бы большого смысла). Вот как вы начинаете:

Довольно просто, не так ли? Мы вызываем this.addMatchers, передавая ему параметр объекта. Каждый ключ в этом объекте станет именем матчера, и связанная с ним функция (значение) будет выполняться. Предположим, мы хотим создать матчер с проверкой, чтобы узнать, находится ли одно число между двумя другими. Вот что вы должны написать:

Мы просто берем два параметра, проверяем, что первый из них меньше второго, и возвращаем логическое выражение, которое выполняется в true, если выполняются наши условия. Здесь важно заметить, как мы получаем значение, которое было передано функции expect: this.actual.

Это то, что делает файл SpecHelper.js; у него есть вызов beforeEach, который добавляет совпадение tobePlaying(). Проверьте это!


Заключение: повеселитесь!

С Jasmine вы многое можете сделать: функциональные тесты, шпионы, асинхронные спецификации и многое другое. Я рекомендую вам изучить вики, если вы заинтересованы. Есть также несколько сопутствующих библиотек, которые облегчают тестирование в DOM: Jasmine-jQuery и Jasmine-fixture (которая зависит от Jasmine-jQuery).

Поэтому, если вы до сих пор еще не тестируете свой JavaScript, сейчас самое подходящее время чтобы начать. Как мы видели, быстрый и простой синтаксис Jasmine делает процесс тестирование довольно простым. Просто уже не осталось причин, чтобы не начать писать тесты, не так ли?

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.