Отбросив все объяснения зачем писать юнит-тесты, перейдем сразу к рассмотрению вопроса как их писать с помощью QUnit.
Для начала скачаем последнюю версию библиотеки с официального сайта(она содержит в себе 2 файла js и css) и создадим обычных HTML файл, в котором будем выполнять наши тесты:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>QUnit Example</title> <link rel="stylesheet" href="qunit.css"> </head> <body> <div id="qunit"></div> <script src="qunit.js"></script> <script src="tests.js"></script> </body> </html>
Файл tests.js с описанием тестов:
QUnit.test( "hello test", function() { ok( 1 == "1", "Passed!" ); });
Запустим HTML файл и посмотрим что у нас получилось.
Разберем этот пример более подробно: QUnit.test – метод, который вызывает тест для определенного функционала, имеет следующий синтаксис:
QUnit.test( checkFunctionName, callback)
причем, каждый функциональный тест может включать в себя множество подтестов-условий, например:
QUnit.test( "hello test", function() { ok( 0 == "0", "Passed!" ); ok( 1 == "1", "Passed!" ); ok( 2 == "2", "Passed!" ); });
Кроме этого мы можем объединять тесты в группы, следующим образом:
module( "group digits" ); QUnit.test( "chech zero", function() { ok( 0 == "0", "Passed!" ); } QUnit.test( "chech one", function() { ok( 1 == "1", "Passed!" ); }
Также QUnit позволяет нам проводить асинхронные тесты, для этого обрамляем условие в метод QUnit.asyncTest и вызвать метод start(), когда когда действие выполнится:
QUnit.asyncTest( "asynchronous test: one second later!", function() { setTimeout(function() { ok( true, "Passed and ready to resume!" ); start(); }, 1000); });
Кроме метода start() для асинхронной работы еще есть и метод stop() – для случая когда нам снова необходимо дождаться отработки асинхронного модуля.
Теперь пройдемся по методам с помощью которых мы создаем условия:
ok( state, message ); //проверят что первое TRUE equal(actual, expected, message);// == strictEqual( actual, expected, message ); // === deepEqual( actual, expected, message ); // === в составном типе throws( block, expected, message ); // для теста exceptions
Также с помощью метода expect(), мы можем уточнить сколько проверок ожидаем сделать в рамках одного теста:
QUnit.asyncTest( "asynchronous test: one second later!", function() { expect(1); setTimeout(function() { ok( true, "Passed and ready to resume!" ); start(); }, 1000); });
и тест будет не пройден, если итоговое число проверок не соответствует заданному.
Библиотека позволяет навешивать callback-функции на различные действия begin, done, log, moduleDone, moduleStart, testDone, testStart:
QUnit.begin(function(){ //do something });
QUnit дает возможность конфигурировать некоторые параметры через QUnit.config:
QUnit.config.reorder = false;
И напоследок бонус – то, чего я не нашел в инете – цепочка асинхронных вызовов. На хабре предложили такой вариант решения:
asyncTest('asynctest', function () { // Pause the test expect(3); $.get(function () { // асинхронные проверки ok(true); }); $.get(function () { // другие асинхронные проверки ok(true); ok(true); }); setTimeout(function () { start(); }, 2000); });
Т.е. ждать 2 секунды, а потом проверять. Сомнительное решение, которое не будет работать, если суммарное время асинхронных вызовов будет больше чем 2 секунды. Может оно конечно так и задумывалось?
Попробуем его немного переделать, так чтобы не нужно было привязываться к конкретному времени ожидания:
asyncTest('asynctest', function () { // Pause the test expect(3); var asyncNumberLeft = 2; function checkEnd(){ asyncNumberLeft--; if(asyncNumberLeft === 0){ start(); } } $.get(function () { // асинхронные проверки ok(true); checkEnd(); }); $.get(function () { // другие асинхронные проверки ok(true); ok(true); checkEnd(); }); });
Но если мы действительно хотим добавить какой-то лимит по времени выполнения, то таймаут следует оставить.