Сегодня я начну серию постов о тестировании Javascript кода. Многие из разработчиков уделяют этому мало времени, считая, что это не стоит внимания, потому что javascript -- это детский язык, который можно выучить за пару дней. Это конечно же ошибка, чтобы понять суть javascript у некоторых уходит достатточно много времени, и они крайне удивляются особенностям javascript, например:
Но это совсем не значит, что я не буду писать о другие посты.
и так поехали.
Jasmine -- это BDD фреймворк для тестирования javascript кода. Довольно простой, не зависит от других фреймворков, то есть был написан для javascript кода, а не для prototype\jquery\yui.
Синтаксис.
Jasmine унаследовало или скорее поддерживает синтаксис основного bdd фреймворка -- RSpec. Тесты состоят из двух частей describe() -- указывает поведение, какой-то общий контекст для которого выполняются тесты. Вторая часть -- it(), простое утверждение. Describe() и it() принимают два аргумента, первый -- это строка, которая показывае, что вы делаете. Второй -- это функция, непосредственно тест.
Проект
Допустим мы скачали jasmine с сайта. Теперь посмотрим какова структура проекта (директорий).
- jasmine-standalone
-- lib
--- jasmine-1.1.0
---- некоторые файлы
-- src
-- //ваши файлы тестируемые
-- spec
-- // ваши тесты
spec_runner.html //запуск тестов, в этот файл скопируйте просто отсюда и пропишите свои пути к файлам тестов и своего кода.
Вот достаточно просто. Для запуска тестов просто открываем spec_runner.html в любом браузере. Смотрим какие прошли, какие не прошли и исправляем.
Написание тестов
Но прежде, чем тестировать нужно что-то написать для тестов. У меня есть вполне реальный пример кода, который я использую, это определитель високосного года.
Работа понятна принимает значение года, возратит true или false, в зависимости от принятого года.
Вот теперь можно и писать тесты:
Дописываем необходимые тесты:
beforeEach & afterEach
Сейчас рассморим работу только beforeEach, так как afterEach будет работать также. Сейчас я напишу некоторую функция-класс, которая будет делать, то же что и isLeapYear:
Напишем тесты, но уже с beforeEach:
Пишем свой mathcer
Написание свойго утверждения в jasmine довольно просто. Может возникнуть зачем новый mather? Всё довольно просто -- это устраняет дублирование кода, а также улучшает кода, ведь не нужно писать большие манипуляции, а достаточно написать один раз новый matcher.
Matcher -- это функция, которая будет принимать сколько угодно аргументов (всё зависит от того, что вы проверяете) и манипулирует переменной this.actual -- это поступает из expect() блока. Matcher добавляется с помощью this.addMatchers({}) в блоке beforeEach(), возвратить ваш matcher должен либо true, либо false, и так когда есть немного теории.
Теперь спеки:
Здесь один тест не пройдёт, что и ожидалось.
---------------------
Что не было рассмотрено: шпионы и асинхронные спеки
"1" + 3 == 13 // true "10" - 3 == 7 // trueЯ напишу несколько постов сначала jasmine, затем фреймворк для Prototypejs & scriptaculous. В серии jasmine будет сначала просто работа с jasmine, потом работа с ajax & DOM, потом интерграция c prototypejs и jquery. Jasmine из Ruby, Потом уже Unit тесты от Томаса Фукса (script.aculo.us)
Но это совсем не значит, что я не буду писать о другие посты.
и так поехали.
Jasmine -- это BDD фреймворк для тестирования javascript кода. Довольно простой, не зависит от других фреймворков, то есть был написан для javascript кода, а не для prototype\jquery\yui.
Синтаксис.
Jasmine унаследовало или скорее поддерживает синтаксис основного bdd фреймворка -- RSpec. Тесты состоят из двух частей describe() -- указывает поведение, какой-то общий контекст для которого выполняются тесты. Вторая часть -- it(), простое утверждение. Describe() и it() принимают два аргумента, первый -- это строка, которая показывае, что вы делаете. Второй -- это функция, непосредственно тест.
Проект
Допустим мы скачали jasmine с сайта. Теперь посмотрим какова структура проекта (директорий).
- jasmine-standalone
-- lib
--- jasmine-1.1.0
---- некоторые файлы
-- src
-- //ваши файлы тестируемые
-- spec
-- // ваши тесты
spec_runner.html //запуск тестов, в этот файл скопируйте просто отсюда и пропишите свои пути к файлам тестов и своего кода.
Вот достаточно просто. Для запуска тестов просто открываем spec_runner.html в любом браузере. Смотрим какие прошли, какие не прошли и исправляем.
Написание тестов
Но прежде, чем тестировать нужно что-то написать для тестов. У меня есть вполне реальный пример кода, который я использую, это определитель високосного года.
function isLeapYear(year) {
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 40 == 0) {
return true;
}
return false;
}
return true;
}
return false;
};
Работа понятна принимает значение года, возратит true или false, в зависимости от принятого года.
Вот теперь можно и писать тесты:
describe("isLeapYear", function() {
it("2004 should be leap year", function() {
expect(isLeapYear(2004)).toBeTruthy();
expect(isLeapYear(2004)).toEqual(true);
});
it("2000 should be leap year", function() {
expect(isLeapYear(2000)).toBeTruthy();
});
it("1700 should not be leap year", function() {
expect(isLeapYear(1700)).toBeFalsy();
});
it("2001 should not be leap year", function() {
expect(isLeapYear(2001)).toBeFalsy();
});
});
Как видим в describe я указал, что будут тесировать функцию isLeapYear, хотя тут можно было указать что угодно. А потом идут утверждения it(). В блоке которых я проверяю с expect(), за expect() идёт некотрое утверждение (matchers), которых несколько в jasmine. Всё довольно просто. Но наши тесты не могут быть полными, ведь я могу отправить вместо числа строку или массив или ничего, поэтому улучшаем функцию.function isLeapYear(year) {
if (typeof(year) != 'number')
throw 'Year should be Number';
...
}
Если тип аргумента не номер, будет брошено исключение.Дописываем необходимые тесты:
it("Not_Number should throws exception", function() {
expect(function() { isLeapYear('Not_Number'); }).toThrow('Year should be Number');
expect(function() { isLeapYear(); }).toThrow('Year should be Number');
});
Всё отлично работает:beforeEach & afterEach
Сейчас рассморим работу только beforeEach, так как afterEach будет работать также. Сейчас я напишу некоторую функция-класс, которая будет делать, то же что и isLeapYear:
var Year = function() {
this.isLeapYear = function(year) {
return isLeapYear(year);
};
};
Видим класс возвращает просто функцию isLeapYear.Напишем тесты, но уже с beforeEach:
describe("Object Year.isLeapYear", function() {
beforeEach(function () {
Obj = new Year();
});
it("2004 should be leap year", function() {
expect(Obj.isLeapYear(2004)).toBeTruthy();
});
});
В beforeEach устанавливаем Obj, который будет экземпляром Year. А в блоке it(), проверяем метод этого объекта Obj.isLeapYear(). Я не писал все тесты, потому что они будут подобны прошлым. Вот результат:Пишем свой mathcer
Написание свойго утверждения в jasmine довольно просто. Может возникнуть зачем новый mather? Всё довольно просто -- это устраняет дублирование кода, а также улучшает кода, ведь не нужно писать большие манипуляции, а достаточно написать один раз новый matcher.
Matcher -- это функция, которая будет принимать сколько угодно аргументов (всё зависит от того, что вы проверяете) и манипулирует переменной this.actual -- это поступает из expect() блока. Matcher добавляется с помощью this.addMatchers({}) в блоке beforeEach(), возвратить ваш matcher должен либо true, либо false, и так когда есть немного теории.
beforeEach(function() {
this.addMatchers({
toBeLeapYear: function () {
var year = this.actual;
if (typeof(year) != 'number')
throw 'Year should be Number';
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 40 == 0) {
return true;
}
return false;
}
return true;
}
return false;
}
});
});
Как видим -- это наша старая функция isLeapYear().Теперь спеки:
it("2004 should be leap year", function() {
expect(2004).toBeLeapYear();
expect(2000).toBeLeapYear();
});
it("2001 fail test", function() {
expect(2001).toBeLeapYear();
});
Здесь один тест не пройдёт, что и ожидалось.
---------------------
Что не было рассмотрено: шпионы и асинхронные спеки



неплохо бы добавить ссылку на SpecRunner.html https://github.com/pivotal/jasmine/blob/master/lib/jasmine-core/example/SpecRunner.html
ОтветитьУдалитьтам есть в "Проект", просто не выделено было. Сейчас я выделил.
Удалить