Введение в Jasmine

Программирование на стороне клиента давно стало нормой, а объем JavaScript кода и его сложность постоянно растут. Часто тестирование применяется только на серверной стороне, но при этом не стоит забывать о тестировании клиентского кода. Для тестирования JavaScript как на стороне клиента, так и для Node.js можно с успехом применять Jasmine.
Jasmine это BDD фреймворк (Behavior-Driven Development — Разработка на Основе Поведений) для тестирования JavaScript кода, позаимствовавший многие черты из RSpec.
Для удобства, будет рассматриваться тестирование в браузере, а для лаконичности примеры приводятся с использованием CoffeeScript (примеры на JavaScript).
Установить Jasmine можно скачав пакет Jasmine standalone. Потребуются файлы:
- lib/jasmine-*/jasmine.js — сам фреймворк
- lib/jasmine-*/jasmine-html.js — оформление результатов в виде HTML
- lib/jasmine-*/jasmine.css — внешний вид результата выполнения тестов
- SpecRunner.html — файл, который следует открыть в браузере для запуска тестов
Основными ключевыми словами при работе с Jasmine являются:
- describe — определение набора тестов, наборы могут быть вложенными
- it — определение теста внутри любого набора тестов
- expect — определяет ожидания, которые проверяются в тесте
Ключевые слова describe и it являются обычными вызовами функций, которым передаются два параметра. Первый — название группы или теста, второй — функция содержащая код. Простой пример для ясности:
describe "Набор тестов", -> it "проверка ожиданий", -> expect(1 + 2).toBe(3)
Для того чтобы отключить выполнение набора тестов или конкретного теста, необходимо воспользоваться ключевыми словами xdescribe и xit соответственно.
describe "Отключение", -> xdescribe "отключенный набор тестов", -> it "тест не будет запущен, так как набор отключен", -> expect(true).toBe(true) xit "отключеный тест", -> expect(true).toBe(true)
Jasmine имеет стандартный набор ожиданий для проверки результатов:
it "сравнение с использованием == сравнение переменных и объектов (включая содержимое)", -> a = b = expect(a).toEqual(b) expect(a).not.toBe(b) # отрицание - a не является b it "значение должно быть определено", -> expect(window.document).toBeDefined() it "значение должно быть не определено", -> expect(window.notExists).toBeUndefined() it "значение должно быть null", -> a = null expect(a).toBeNull() it "значение должно быть верно", -> expect(5 > 0).toBeTruthy() it "значение должно быть не верно", -> expect(5 < 0).toBeFalsy() it "значение должно быть меньше чем", ->expect(1 + 2).toBeLessThan(5) it "значение должно быть больше чем", -> expect(1 + 2).toBeGreaterThan(0) it "значение должно быть близко к числу", -> expect(1.2345).toBeCloseTo(1.2, 1) it "значение должно соответствовать регулярному выражению", -> expect("some string").toMatch(/string/) it "значение должно содержать", -> expect([1, 2, 3]).toContain(2) expect("some string").toContain("some") it "должно быть вызвано исключение", -> func = -> window.notExists.value expect(func).toThrow()
Для того чтобы избежать повторения при создании/удалении объектов и загрузки фикстур, необходимых для выполнения тестов, используются функции beforeEach/afterEach. Они запускаются перед/после каждого теста в наборе.
describe "Подготовка/чистка", -> val = 0 # определение переменной в зоне видимости набора тестов beforeEach -> val += 1 afterEach -> val = 0 it "использует val", -> expect(val).toEqual(1)
Jasmine поддерживает тестирование асинхронных вызовов с помощью функций runs и waitsFor.
- runs — принимает асинхронную функцию для выполнения.
- waitsFor — принимает первым параметром функцию, которая должна вернуть true, если асинхронный вызов сделанный в runs был выполнен, второй параметр — сообщение об ошибке, третий — время ожидания в миллисекундах.
describe "Асинхронно", -> a = 0 async = -> setTimeout((-> a = 5), 1000) it "асинхронное выполнение кода", -> runs(-> async()) waitsFor((-> a == 5), "значение должно быть изменено", 3000)
Рассмотрение работы со “шпионами” spies (mock object) и работы со временем (mock clock) оставим для следующей статьи. Если у Вас есть вопросы или замечания, буду рад на них ответить.
Мануал по запуску тестов при помощи Karma и Jasmine

Karma — это консольный инструмент для запуска тестов, который умеет следить за изменениями исходного кода и отображать процент покрытия кода тестами. Настраивается с помощью конфигурационного файла karma.conf.js, в котором нужно указать пути к файлам, которые будут тестироваться, и пути к файлам, содержащие тесты.
Консольные команды
karma init — создаёт базовый шаблон файла конфигурации. Его можно также скачать с репозитория.
karma start — запуск
Jasmine
Jasmine — фреймворк для написания js-тестов.
Основной синтаксис фреймворка:
describe() — оборачивает тесты в test-suite;
beforeEach() и afterEach() — соответственно, запускаются для каждого теста;
it(‘название теста’, function()<>) — сам тест;
iit() и xit() — выделяют тест, чтобы прогнать только его или игнорировать его соответственно.
Пример теста

Установка и настройка компонентов
Скачивать компоненты мы будем с помощью пакетного менеджера npm, который входит в состав Node.js. Он также необходим для работы Karma.
После установки Node.js, переходим в корень проекта, вызываем консоль и поочерёдно выполняем команды. Все скачанные компоненты будут располагаться в каталоге node_modules.
Karma
npm install -g karma-cli
npm install phantomjs -g — эмулятор для тестирования javascript кода.
npm install karma-jasmine karma-chrome-launcher karma-phantomjs-launcher —save — karma-плагины для запуска тестов, написанных на Jasmine, для запуска в браузерах Chrome и PhantomJS.
npm install angular-mocks — библиотека с mock-объектами для сервисов AngularJS. С их помощью мы сможем тестировать работу сервиса $http, не отправляя запросов на сервер.
Jasmine
npm install -g jasmine
Чтобы использовать фреймворк Jasmine, подключите к проекту следующие файлы:
jasmine.js — сам фреймворк;
jasmine-html.js — оформление результатов в виде HTML;
jasmine.css — внешний вид результата выполнения тестов.
Также можно использовать следующие команды:
jasmine init — инициализирует Jasmine проект;
jasmine examples — помещает в Jasmine проект примеры с тестами.
Настройка файла конфигурации Karma
Нужно убедиться, что для Karma прописана переменная среды ОС. Создадим файл с именем karma.conf.js. Для этого нужно в консоли выполнить команду
karma init karma.conf.js.

Параметры karma.config.js:
В скобках () для каждого параметра указано значение по умолчанию:
files([]) — список файлов для загрузки. Массив тех файлов, которые будут подключены в браузер для запуска тестов;
exclude([]) — список исключений для предыдущего пункта;
reporters([‘progress’]) — вариант вывода прогресса;
port(8080) — порт веб-сервера;
runnerPort(9100) — порт клиента;
colors(true) — включение/выключение цветов при выводе лога в консоль;
logLevel(LOG_INFO) — LOG_DISABLE|LOG_ERROR|LOG_WARN|LOG_INFO|LOG_DEBUG;
autoWatch(false) — выполнение тестов при изменении файлов;
browsers([]) — Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS;
captureTimeout(5000) — задание таймаута в миллисекундах;
singleRun(false) — для одноразового запуска;
preprocessors(<>) — список обработчиков, которые будут применены к файлам, до загрузки в браузер.
Jasmine 2 — Внедряем юнит-тесты на Jasmine, в процесс разработки приложения
Посмотрев это видео и применив увиденное на практике, вы получите реальный опыт юнит-тестирования приложения! Вы будете писать методы приложения, основываясь на тестах которые вы будете описывать по методологии BDD с помощью библиотеки Jasmine.
* в методе install, конечно же стоит убрать лишний цикл и написать 2 условия в одном.
Мы будем писать приложение Computer, которое будет уметь увеличивать количество оперативной памяти, устанавливать программы, а также форматировать жесткий диск. Создаем два файла computer_spec.js и computer.js в папках spec и src соответственно. После чего открываем первый файл и приступаем к первым тестам.
it(‘должен иметь возможность увеличивать свою оперативную память’, function()
expect( Computer.addMemory(6) ).toEqual(8);
it(‘должен показывать текущее количество памяти’, function()
expect( Computer.currentMemoryGBs ).toBeDefined();
expect( Computer.currentMemoryGBs ).toEqual(2);
it(‘должен запрещать добавлять больше двух плат памяти’, function()
expect( Computer.addMemory(6,8) ).toEqual(16);
expect( Computer.addMemory(6,8,4) ).toBe(false);
Функция beforeEach, задает свойства нашего компьютера по умолчанию перед каждым новым тестом, для того чтобы результаты тестов не конфликтовали. После того как мы описали тесты и поняли какие результаты должны получить, начинаем писать соответствующий метод и свойство приложения.
var memory = this.currentMemoryGBs;
for (var i = 0, len = arguments.length; i < len; i++)
Путем такого поставления утверждений через юнит-тесты, становится проще писать и само приложение, так как точны ясны конечная цель и результат. Иными словами вы можете сначала описать весь функционал и логику вашего приложения в тестах и только потом приступать к написанию самого приложения, так вы никогда не пропустите и не забудете того, что ваше приложение должно уметь. Когда пишете тесты, представляйте их как требования пользователя к вашему приложению.
Давайте опишем тесты, которые помогут нам написать метод установки программ на компьютер. Заодно разобьем тесты на группы.
describe(‘когда увеличивает оперативную память’, function()
it(‘должен иметь возможность увеличивать свою оперативную память’, function()
expect( Computer.addMemory(6) ).toEqual(8);
it(‘должен показывать текущее количество памяти’, function()
expect( Computer.currentMemoryGBs ).toBeDefined();
expect( Computer.currentMemoryGBs ).toEqual(2);
it(‘должен запрещать добавлять больше двух плат памяти’, function()
expect( Computer.addMemory(6,8) ).toEqual(16);
expect( Computer.addMemory(6,8,4) ).toBe(false);
describe(‘когда устанавливает программы’, function()
it(‘должен уметь инсталлировать любое количество программ’, function()
it(‘должен запрещать устанавливать программы, когда место на жестком диске закончилось’, function()
Теперь мы знаем, что должен уметь компьютер при установке программ, напишем метод install.
for (var i = 0, len = arguments.length; i < len; i++)
if ( arguments[i].installed === false )
Теперь остался последний метод нашего компьютера, форматирование жесткого диска. Опишем для него тест.
it(‘должен уметь форматировать жесткий диск’, function()
expect( Computer.currentSpaceGBs ).toEqual(1000);
Пришло время и для самого метода formatDrive, он будет очень простым.
Ну вот и все, вот так, через описание тестов мы написали наше мини-приложение компьютера. Поначалу может показаться, что юнит-тесты излишняя трата времени, но попробуйте на практике сами и убедитесь в том, как BDD может улучшить качество вашего кода, а так же его поддержку, тестирование и расширяемость. C юнит-тестами вы всегда сможете увидеть не поломалось ли что из старых функций, после изменения вашего кода и добавления нового функционала. Если что то сломалось, вы сразу увидите красный экран Jasmine, если все в порядке — зеленый. На сегодня это всё, всем удачного тестирования!
Юнит-тесты на Jasmine
Сегодня поговорим о написании юнит тестов для javascript на фреймворке Jasmine.
Так как я запускаю тесты через Karma, то мне для работы с Jasmine понадобилось просто установить плагин karma-jasmine. Но случаи могут быть разные, подробно про установку расписано в репозитории Jasmine на Github.
Итак, в юнит-тестах мы проверяем — соответствует ли поведение кода тому, которое мы ожидаем.
Пример теста
Структура кода довольно простая:
describe("Проверка функции Sum(a,b)", function() < it("Sum(2,5) должно вернуть 7", function() < expect(Sum(2,5)).toBe(7); >); >);
Мы видим описание блока тестов, условие выполнения конкретного теста и наконец проверку самого утверждения:
expect(Sum(2,5)).toBe(7);
Естественно .toBe() не единственный метод, в документации рассматриваются и другие. Но на некоторые моменты я бы еще обратила внимание.
beforeEach и afterEach
Иногда возникает необходимость выполнить какие-либо действия перед каждым тестом блока.
В таком случае используем beforeEach().
А для действий после каждого теста блока используем afterEach().
var b = 0; describe("Проверим как работает BeforeEach и afterEach", function() < beforeEach(function () < b = 1; >); afterEach(function () < b = 0; >); it("b должно быть равно 1", function () < expect(b).toEqual(1); >); >); describe("Проверим как работает afterEach", function() < it("b должно быть равно 0", function () < expect(b).toEqual(0); >); >);
Все тесты выше выполнятся.
beforeAll()
Если хотим выполнить какое-либо действие один раз перед началом тестов блока, то используем beforeAll().
Например, может быть удобно создать объект один раз перед всеми тестами блока и проверять затем по очереди его методы.
beforeAll(function() < my_bicycle = new Bicycle('Stels','red'); >);
Асинхронность
Jasmine поддерживает асинхронность. Иногда нам необходимо, чтобы тесты продолжили выполняться только после завершения какого-либо действия. Допустим мы по Ajax должны получить данные необходимые для тестов. Тогда мы используем done функцию:
beforeAll(function(done) < $.when( $.ajax(API_URL + 'data.json') ).then(function(data) < testData = data; done(); >); >);
Пока не вызовется done() тесты не продолжатся, а она вызовется только после получения json данных.
Тестирование DOM
И наконец бывает нам нужно потестировать функции связанные с DOM, либо проверить загрузились ли какие-то элементы в DOM после выполнения кода.
В таком случае понадобится плагин jasmine-jquery.
На странице плагина хорошие примеры, но один рассмотрим.
Допустим нам надо потестировать корректно ли работает функция привязки обработчика к событиям show и hide, которую я рассматривала в посте про обработку событий.
Код будет такой:
it ("show-hide handler should work", function() < setFixtures(' );
Сначала мы устанавливаем ‘фихтуру’ — просто создаем div для теста. Далее создаем шпиона на событие, затем вызываем событие и наконец проверяем, что событие на элементе сработало.
Тесты можно писать и на coffescript, также как и тестировать мы можем код написанный на coffescript.
Конечно, в таком случае перед запуском тестов необходимо переводить файлы в javascript, но об этом, а также о запуске тестов и сборке необходимых файлов с помощью Karma и Gulp в следующем посте Юнит-тесты: Jasmine Karma Gulp .