Вчимося тестувати JavaScript код. Модульне тестування коду на мові JavaScript: стратегії, бібліотеки, інструменти Тестування js

На прикладі простого додатка-калькулятора на Node.js. Тестувати будемо за допомогою фреймворка Mocha.

Що повинно вміти наш додаток:

  • Додавати, віднімати, ділити і множити будь-які два числа;
  • Показувати попередження і завершувати роботу, якщо було введено щось відмінне від числа;
  • Також повинен бути інтерфейс командного рядка, щоб кінцевий користувач міг скористатися додатком.

Що нам буде потрібно:

  • Node.js і npm;
  • Знання JavaScript: синтаксис і структура коду, типи даних, математичні операції і умовні вирази.

З цілями розібралися, можна приступати до налаштування середовища для тестування і розробки.

налаштовуємо середу

Так як ми використовуємо Node.js, потрібно створити локальне оточення для файлів і залежностей.

Створіть нову папку calc. У командному рядку перейдіть в цю директорію і створіть новий проект командою npm init, яка створить новий файл package.json для нашої програми.

Вам запропонують ввести ім'я пакунку, встановлену версію опис та іншу інформацію про пакет. Ви можете ввести ім'я calc.js і далі тиснути Enter для присвоєння значень за замовчуванням. Коли ви дійдете до test command, введіть mocha - це фреймворк для тестування, який ми будемо використовувати:

test command: mocha

Після введення всієї інформації скрипт створить файл package.json, Який виглядає приблизно так:

( "Name": "calc.js", "version": "1.0.0", "description": "Простий калькулятор на Node.js", "main": "index.js", "scripts": ( " test ":" mocha ")," author ":" "," license ":" ISC ")

Останній крок на даному етапі - установка Mocha. Введіть наступну команду для установки:

Npm install --save-dev mocha

Після застосування цієї команди з'явиться папка node_modules, файл package-lock.json, А в файлі package.json з'являться такі рядки:

"DevDependencies": ( "mocha": "^ 4.0.1")

створіть файл test.js. Ми скористаємося вбудованим в Node.js модулем assert, Щоб перевірити вірність рівності true і true. Так як воно вірно, тест повинен пройти успішно:

Const assert \u003d require ( "assert"); it ( "повинно повертати true", () \u003d\u003e (assert.equal (true, true);));

Тепер запустіть тест з командного рядка:

$ Npm test\u003e mocha ✓ має повертати true 1 passing (8ms)

Тест пройшов як і очікувалося, тому з налаштуванням середовища покінчено. видаліть з test.js все, крім рядка const assert \u003d require ( "assert"); .

Ми будемо використовувати файл test.js протягом усього процесу створення програми. Створіть ще два файли: operations.js для арифметичних і валідаційних функцій і calc.js для самого додатка. Ми використовуємо так багато файлів, щоб вони не ставали занадто довгими і складними. Ось наш поточний список файлів:

  • calc.js;
  • node_modules;
  • operations.js;
  • package-lock.json;
  • package.json;
  • test.js;

Давайте додамо перший справжній тест для нашого застосування.

Додаємо математичні операції

Перш за все, наше додаток повинен вміти складати, віднімати, ділити і множити будь-які два числа. Значить, для кожної з цих операцій ми повинні створити окрему функцію.

Почнемо зі складання. Ми напишемо тест, в якому однозначно вийде очікувана сума двох чисел. У коді нижче ми перевіряємо, чи дорівнює сума 1 і 3 за допомогою функції add () 4:

Const assert \u003d require ( "assert"); it ( "правильно знаходить суму 1 і 3", () \u003d\u003e (assert.equal (add (1, 3), 4);));

Після запуску тесту за допомогою команди npm test ми бачимо наступне:

\u003e Mocha 0 passing (9ms) 1 failing 1) правильно знаходить суму 1 і 3: ReferenceError: add is not defined at Context.it (test.js: 5: 16) npm ERR! Test failed. See above for more details.

Тест провалився з повідомленням ReferenceError: add is not defined. Ми тестуємо функцію add (), якою ще немає, тому такий результат цілком очікуємо.

Створимо функцію add () у файлі operations.js:

Const add \u003d (x, y) \u003d\u003e (+ x) + (+ y);

Ця функція приймає два аргументи x і y і повертає їх суму. Ви могли помітити, що ми пишемо (+ x) + (+ y), а не x + y. Ми використовуємо унарний оператор для приведення аргументу до числа, на випадок, якщо введення буде рядком.

Примітка Тут використовується додана в ES6 стрелочная функція і неявний повернення.

Так як ми використовуємо Node.js і розбиваємо код на безліч файлів, потрібно скористатися module.exports, щоб експортувати код:

Const add \u003d (x, y) \u003d\u003e (+ x) + (+ y); module.exports \u003d (add)

На початку файлу test.js ми імпортуємо код з operations.js за допомогою require (). Так як ми використовуємо функцію через змінну operations, потрібно поміняти add () на operations.add ():

Const operations \u003d require ( "./ operations.js"); const assert \u003d require ( "assert"); it ( "правильно знаходить суму 1 і 3", () \u003d\u003e (assert.equal (operations.add (1, 3), 4);));

Запускаємо тест:

$ Npm test\u003e mocha ✓ правильно знаходить суму 1 і 3 1 passing (8ms)

Тепер у нас є працююча функція, і тести проходять успішно. Так як функції інших операцій працюють схожим чином, додати тести для subtract (), multiply () і divide () не складе труднощів:

It ( "правильно знаходить суму 1 і 3", () \u003d\u003e (assert.equal (operations.add (1, 3), 4);)); it ( "правильно знаходить суму -1 і -1", () \u003d\u003e (assert.equal (operations.add (-1, -1), -2);)); it ( "правильно знаходить різницю 33 і 3", () \u003d\u003e (assert.equal (operations.subtract (33, 3), 30);)); it ( "правильно знаходить твір 12 і 12", () \u003d\u003e (assert.equal (operations.multiply (12, 12), 144);)); it ( "правильно знаходить приватна 10 і 2", () \u003d\u003e (assert.equal (operations.divide (10, 2), 5);));

Тепер створимо і експортуємо всі функції в test.js:

Const add \u003d (x, y) \u003d\u003e (+ x) + (+ y); const subtract \u003d (x, y) \u003d\u003e (+ x) - (+ y); const multiply \u003d (x, y) \u003d\u003e (+ x) * (+ y); const divide \u003d (x, y) \u003d\u003e (+ x) / (+ y); module.exports \u003d (add, subtract, multiply, divide,)

І запустимо нові тести:

$ Npm test\u003e mocha ✓ правильно знаходить суму 1 і 3 ✓ правильно знаходить суму -1 і -1 ✓ правильно знаходить різницю 33 і 3 ✓ правильно знаходить твір 12 і 12 ✓ правильно знаходить приватна 10 і 2 5 passing (8ms)

Всі тести проходять успішно, тому тепер ми можемо бути впевнені, що основні функції нашого додатки будуть працювати коректно. Тепер можна зайнятися додатковою валідацією.

додаємо валідацію

На даний момент, коли користувач вводить число і вибирає потрібну операцію, все працює нормально. Однак що трапиться, якщо спробувати знайти суму числа і рядки? Додаток спробує виконати операцію, але через те, що воно очікує числа, воно поверне NaN.

Замість того щоб повертати якісь незрозумілі значення, пора виконати друге завдання - зробити так, щоб додаток показувало попередження і завершувало свою роботу, якщо введений аргумент не є числом.

Спочатку потрібно написати функцію, яка буде перевіряти, чи є введення числом чи ні. Додаток має працювати тільки з числами, тому ми будемо обробляти три ситуації:

  1. Обидва введення - числа.
  2. Один введення - число, а інший - рядок.
  3. Обидва введення - рядки.
it ( "повідомляє про помилку при використанні рядка замість числа", () \u003d\u003e (assert.equal (operations.validateNumbers ( "sammy", 5), false);)); it ( "повідомляє про помилку при використанні двох рядків замість чисел", () \u003d\u003e (assert.equal (operations.validateNumbers ( "sammy", "sammy"), false);)); it ( "успіх при використанні двох чисел", () \u003d\u003e (assert.equal (operations.validateNumbers (5, 5), true);));

Функція validateNumbers () буде перевіряти обидва параметри. Функція isNaN () перевіряє, чи не є параметр числом, і якщо немає, то повертає false. В іншому випадку вона повертає true, що означає успішну валідацію.

Const validateNumbers \u003d (x, y) \u003d\u003e (if (isNaN (x) && isNaN (y)) (return false;) return true;)

Не забудьте додати validateNumbers в module.exports в кінці файлу. Тепер можна запускати нові тести:

$ Npm test 1) повідомляє про помилку при використанні рядка замість числа ✓ повідомляє про помилку при використанні двох рядків замість чисел ✓ успіх при використанні двох чисел 7 passing (12ms) 1 failing 1) повідомляє про помилку при використанні рядка замість числа: AssertionError: true \u003d\u003d false + expected - actual -true + false

Два тесту пройшли, але один провалився. Перевірка на введення двох чисел пройшла успішно, так само як і перевірка на введення двох рядків. Чого не можна сказати про перевірку на введення рядка і числа.

Якщо поглянути на нашу функцію ще раз, то можна помітити, що обидва параметра повинні бути NaN, щоб функція повернула false. Якщо ми хочемо домогтися того ж ефекту, коли хоча б один з параметрів дорівнює NaN, потрібно замінити && на || :

Const validateNumbers \u003d (x, y) \u003d\u003e (if (isNaN (x) || isNaN (y)) (return false;) return true;)

Якщо після цих змін знову запустити npm test, то всі тести пройдуть успішно:

✓ повідомляє про помилку при використанні рядка замість числа ✓ повідомляє про помилку при використанні двох рядків замість чисел ✓ успіх при використанні двох чисел 8 passing (9ms)

Ми протестували всю функціональність нашого застосування. Функції успішно виконують математичні операції і перевіряють введення. Фінальний етап - створення призначеного для користувача інтерфейсу.

створюємо інтерфейс

Потрібні функції у нас вже є, але користувач поки що ніяк не може ними скористатися. Тому нам потрібен інтерфейс. Для нашого застосування ми створимо інтерфейс командного рядка.

На даний момент файл calc.js повинен бути порожній. Тут і буде зберігатися наш додаток. Спочатку потрібно імпортувати функції з operations.js:

Const operations \u003d require ( "./ operations.js");

Сам інтерфейс буде використовувати вбудований в Node.js CLI-модуль Readline:

Const readline \u003d require ( "readline");

Після імпортування всього, що потрібно, можна приступити до створення програми. Для створення інтерфейсу ми будемо використовувати readline, доступний через змінну rl:

Const rl \u003d readline.createInterface ((input: process.stdin, output: process.stdout));

Перше, що користувач повинен бачити після запуску програми, - вітальне повідомлення та інструкції по використанню. Для цього ми скористаємося console.log ():

Console.log ( `Calc.js Ви відкрили калькулятор на Node.js! Версія: 1.0.0. Використання: користувач повинен ввести два числа, а потім вибрати, що з ними зробити.`);

Перш ніж ми займемося самими функціями калькулятора, давайте перевіримо, що console.log () працює як треба. Ми зробимо так, щоб програма виводила повідомлення і завершувала роботу. Для цього додайте в кінці виклик методу rl.close ().

Щоб запустити додаток, введіть node і ім'я файлу:

$ Node calc.js Calc.js Ви відкрили калькулятор на Node.js! Версія: 1.0.0. Використання: користувач повинен ввести два числа, а потім вибрати, що з ними зробити.

Програма виводить вітальне повідомлення і завершує свою роботу. Тепер потрібно додати користувача введення. Від користувача зверніть увагу на таке: вибрати два числа і одну операцію. Кожен введення буде запитуватися методом rl.question ():

Rl.question ( "Введіть перше число:", (x) \u003d\u003e (rl.question ( "Введіть друге число:", (y) \u003d\u003e (rl.question ( `Виберіть одну з наступних операцій: Додавання (+) Віднімання (-) Множення (*) Розподіл (/) Ваш вибір: `, (choice) \u003d\u003e (// тут ще з'явиться код rl.close ();));));));

Змінної x присвоюється перше число, y - друга, а choice - обрана операція. Тепер наша програма запитує введення, але нічого не робить з отриманими даними.

Після третього введення потрібно перевірити, що були введені тільки числа. Для цього скористаємося функцією validateNumbers (). За допомогою оператора НЕ ми перевіримо, чи були введені числа, і, якщо це не так, завершимо роботу програми:

If (! Operations.validateNumbers (x, y)) (console.log ( "Можна вводити тільки числа! Будь ласка, перезапустіть програму.");)

Якщо все введено вірно, то тепер потрібно запустити відповідний операції метод, створений раніше. Для обробки чотирьох можливих варіантів вибору ми скористаємося виразом switch і виведемо результат операції. Якщо була обрана неіснуюча операція, буде виконаний блок default, щоб повідомити користувача про необхідність повторити спробу:

If (! Operations.validateNumbers (x, y)) (console.log ( "Можна вводити тільки числа! Будь ласка, перезапустіть програму.");) Else (switch (choice) (case "1": console.log ( `Сума $ (x) і $ (y) дорівнює $ (operations.add (x, y)). `); break; case" 2 ": console.log (` Різниця $ (x) і $ (y) дорівнює $ ( operations.subtract (x, y)). `); break; case" 3 ": console.log (` Твір $ (x) і $ (y) дорівнює $ (operations.multiply (x, y)). `) ; break; case "4": console.log ( `Приватне $ (x) і $ (y) дорівнює $ (operations.divide (x, y)).`); break; default: console.log ( "Будь ласка, перезапустіть програму і виберіть число від 1 до 4. "); break;))

Примітка Тут у функціях console.log () використовуються шаблонні рядки, що дозволяють застосування виразів.

/ ** * Простий калькулятор на Node.js, який використовує calculator app that uses * вбудований інтерфейс командного рядка Readline. * / Const operations \u003d require ( "./ operations.js"); const readline \u003d require ( "readline"); // Використовуємо readline для створення інтерфейсу const rl \u003d readline.createInterface ((input: process.stdin, output: process.stdout)); console.log ( `Calc.js Ви відкрили калькулятор на Node.js! Версія: 1.0.0. Використання: користувач повинен ввести два числа, а потім вибрати, що з ними зробити.`); rl.question ( "Введіть перше число:", (x) \u003d\u003e (rl.question ( "Введіть друге число:", (y) \u003d\u003e (rl.question ( `Виберіть одну з наступних операцій: Додавання (+) Віднімання (-) Множення (*) Розподіл (/) Ваш вибір: `, (choice) \u003d\u003e (if (! operations.validateNumbers (x, y)) (console.log (" Можна вводити тільки числа! Будь ласка, перезапустіть програму. ");) else (switch (choice) (case" 1 ": console.log (` Сума $ (x) і $ (y) дорівнює $ (operations.add (x, y)). `); break; case "2": console.log ( `Різниця $ (x) і $ (y) дорівнює $ (operations.subtract (x, y)).`); break; case "3": console.log ( `Твір $ ( x) і $ (y) дорівнює $ (operations.multiply (x, y)). `); break; case" 4 ": console.log (` Приватне $ (x) і $ (y) дорівнює $ (operations. divide (x, y)). `); break; default: console.log (" Будь ласка, перезапустіть програму і виберіть число від 1 до 4. "); break;)) rl.close ();));)) ;));

Тепер наше додаток готове. Перевіримо його роботу наостанок. Введемо 999 і 1 і виберемо операцію віднімання:

$ Node calc.js Введіть перше число: 999 Введіть друге число: 1 Ваш вибір: 2 Різниця 999 і 1 дорівнює 998.

Програма успішно завершила свою роботу, вивівши правильний результат. Вітаємо, ви написали простий калькулятор за допомогою Node.js і вивчили основи TDD-розробки.

Створення ефективних тест-кейсів може бути вкрай важливим для великих проектів, у разі, якщо поведінка частин програми може змінюватися з різних причин. Мабуть, найбільш частою є проблема, коли велика група розробників працюють над одним і тим же, або суміжними модулями. Це може призводити до незапланованого зміни поведінки функцій, написаних іншими програмістами. Або робота в стислі терміни призводить до ненавмисному зміни критичних частин програми.

Тестування веб-додатки як правило полягає у візуальній оцінці елементів сторінки і емпіричної оцінки працездатності функціоналу. Іншими словами в переході по розділах і здійсненні дій над динамічними елементами.

Згодом проект наповнюється новими функціональними можливостями, що подовжує і ускладнює процес перевірки його роботи. Для автоматизації використовуються модульне (unit) тестування.

Існують 2 підходи до побудови тестових сценаріїв:

  • WhiteboxTesting - написання тестів грунтується на реалізації функціоналу. Тобто на ми перевіряємо за тими ж алгоритмами, на яких будуватиметься роботи модулів нашої системи. Такий підхід не гарантує коректність роботи системи в цілому.
  • BlackboxTesting - створення сценаріїв базується на специфікаціях і вимогах до системи. Так можна перевірити правильність результатів роботи всього програми, однак подібний підхід не дозволяє відловити дрібні і рідкісні помилки.

що тестувати

Може здатися, що варто піддавати проведення тестів кожну функцію, яку ви реалізували. Це не зовсім так. Написання тестів займає час розробника, тому для оптимізації процесу роботи над створенням програми варто готувати тести лише складних, критичних, або тих функцій, які залежать від результатів роботи інших модулів системи. Покривайте тестами неоднозначну логіку, в якій потенційно можуть виникати помилки. Також варто створювати тести для тих ділянок коду, які в майбутньому планується оптимізувати, щоб вже після процесу оптимізації можна було переконатися в коректності їх виконання.

Взагалі вкрай важливо оцінювати витрати на проведення тестування щодо стислість термінів з розробки. Звичайно, якщо ви не обмежені в часі, то можете дозволити покрити тестами кожну функцію. Але як правило розробка ведеться в жорсткому цейтноті, так що завдання аналітика або досвідченого розробника зрозуміти, де проведення тестування необхідно. Крім того, написання тестів збільшує вартість проекту.

Таким чином можна сформулювати 3 випадки, коли використання модульного тестування виправдано:

1) Якщо тести дають можливість швидше виявити помилки, ніж при звичайному їх пошуку.

2) Знижують час на налагодження

3) Дозволяють тестувати часто змінюваний код.

З 3х основних компонент фронтендів (HTML, CSS, JavaScript) тестувати потрібно, мабуть лише JavaScript-код. CSS перевіряється виключно візуальним методом, коли розробник / тестувальник / замовник переглядає графічний інтерфейс в різних браузерах. HTML - розмітка перевіряється тим же методом.

як тестувати

При побудові сценаріїв проведення тестів варто керуватися наступними принципами:

  • Ваші тести повинні бути максимально простими. Тоді буде більша ймовірність того, що на результати його проведення буде впливати саме той баг, який ви і намагаєтеся повторити.
  • Декомпозіруется тести великих модулів. Гаю знайти конкретне місце помилки.
  • Робіть тести незалежними. Результат одного тесту ні в якому разі не повинен залежати від результатів іншого.
  • Результати проведення тестів повинні бути повністю повторюваними і очікуваними.Кожен раз, коли ви знову запускаєте тест, його результат повинен бути тим же самим, що і минулого разу.
  • Для будь-якої помилки у виконанні програми повинен створений сценарій тестування.Таким чином ви будете впевнені, що баг дійсно виправлений і не проявляється у користувачів.

чим тестувати

Для unit-тестування js-коду існують кілька бібліотек. Мабуть найпоширенішою є QUnit. Для проведення модульних тестів за допомогою цієї бібліотеки нам буде потрібно створити «пісочницю» - просту html-сторінку, в якій будуть підключена бібліотека для тестування, код, якій потрібно піддати тестам, і власне самі тести.

Функції для тестів:

(Function () (window.stepen \u003d function (int) (var result \u003d 2; for (var i \u003d 1; i< int; i ++) { result = result * 2; } return result; } window.returnFunc = function() { return "ok"; } })();

Лістинг тестів:

Test ( "stepen ()", function () (equal (stepen (2), 4, "2 ^ 2 - equal method"); ok (stepen (3) \u003d\u003d\u003d 8, "2 ^ 3 - ok method" ); deepEqual (stepen (5), 32, "2 ^ 5 - deepEqual method");)); asyncTest ( "returnFunc ()", function () (setTimeout (function () (equal (returnFunc (), "ok", "Async Func Test"); start ();) 1000);));

Як видно, QUnit підтримує 3 функції для порівняння результатів виконання коду з очікуваним:

  • ok () - вважає тест успішним, якщо повертається результат \u003d true
  • equal () - порівнює результат з очікуваним
  • deepEqual () - порівнює результат з очікуваним, перевіряючи його тип

Результат виконання:

Як видно, бібліотека QUnit проводить тестування коду відразу для декількох браузерів.

Існує ряд інших бібліотек для модульних тестів. Однак концепція побудови сценаріїв тестування в них та ж сама, так що розібравшись з однієї - у вас не складе труднощів перейти на іншу.

важливо пам'ятати

Особливістю сучасного js-коду є асинхронність його виконання. Бібліотеки для тестування як правило мають можливість проведення асинхронних тестів. Але наприклад якщо ви намагаєтеся протестувати функцію, яка, скажімо, посилає get-запит на бекенда і повертає відповідь від нього, то для проведення тестів доведеться зупиняти потік функцією stop (), запускати тестируемую функцію, а потім заново запускати потік методом start () , «обернувши його» в setTimeout (). Тобто ви повинні закласти якийсь проміжок часу, протягом якого має завершитися виконання функції. Потрібно ретельно вибирає тривалість цього відрізка, .до. з одного боку довга робота методу може бути як особливість або навіть необхідністю конкретної реалізації функціоналу додатка, так і некоректною поведінкою.

Тестування Backbone додатків

Для прикладу тестування додатків, написаних з використанням Backbone.js скористаємося проектом, описаним в.

Модульними тестами можна перевірити:

  • Коректність створення моделей і контролерів
  • Правильність даних в моделях
  • Виконання методів контролерів (для цього вони повинні повертати результат)
  • Успішність завантаження уявлень

Код тестів:

Test ( "Backbone.js", function () (ok (sample, "Namespace check"); ok (sample.routers.app, "Router check"); ok (sample.core.pageManager.open ( "chat") , "Page opening test (Controller method call)") ok (sample.core.state, "Model check"); equal (sample.core.state.get ( "content"), "sintel", "Model data get test "); stop (); ok (function () ($ .ajax ((url:" app / templates / about.tpl ", dataType:" text ")). done (function (data) (self. $ el. html (data); return data;))), "Template loading check"); setTimeout (function () (start ();) 1000);));

Результат роботи з помилками тестування:

Автоматизація запуску тестів

Як правило розгортання програми є завданням, яке доводиться виконувати досить часто при інтенсивній розробці. Тому цю операцію як правило автоматизують. Ми в своїй роботі використовуємо Jenkins - інструмент для безперервної інтеграції. Ідея полягає в поєднанні деплоя через Jenkins з проведенням автоматичних тестів.

QUnit тести запускаються в браузері. Обійти цю особливість нам допоможе phantomjs - ПО, що емулює роботу браузера. Розробники phantomjs вже надали скрипт для виконання QUnit тестів, проте для коректної роботи довелося трохи його доопрацювати.

/ ** * Wait until the test condition is true or a timeout occurs. * Useful for waiting * on a server response or for a ui change (fadeIn, etc.) to occur. * * @Param testFx javascript condition that evaluates to a boolean, * it can be passed in as a string (eg: "1 \u003d\u003d 1" or * "$ (" # bar "). Is (": visible ")" or * as a callback function. * @param onReady what to do when testFx condition is fulfilled, * it can be passed in as a string (eg: "1 \u003d\u003d 1" or * "$ (" # bar "). is ( ": visible") "or * as a callback function. * @param timeOutMillis the max amount of time to wait. If not * specified, 3 sec is used. * / function waitFor (testFx, onReady, timeOutMillis) (var maxtimeOutMillis \u003d timeOutMillis? timeOutMillis: 3001, //< Default Max Timout is 3s start = new Date().getTime(), condition = false, interval = setInterval(function() { if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { // If not time-out yet and condition not yet fulfilled condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code } else { if(!condition) { // If condition still not fulfilled // (timeout but condition is "false") console.log(""waitFor()" timeout"); phantom.exit(1); } else { // Condition fulfilled (timeout and/or condition is //"true") console.log(""waitFor()" finished in " + (new Date().getTime() - start) + "ms."); typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it"s supposed to do once the // condition is fulfilled clearInterval(interval); //< Stop this interval } } }, 100); // repeat check every 250ms }; }; if (phantom.args.length === 0 || phantom.args.length > 2) console.log ( "Usage: run-qunit.js URL"); phantom.exit (); ) Var page \u003d new WebPage (); // Route "console.log ()" calls from within the Page // context to the main Phantom context (i.e. current "this") page.onConsoleMessage \u003d function (msg) (console.log (msg);); page.open (phantom.args, function (status) (if (status! \u003d\u003d "success") (console.log ( "Unable to access network"); phantom.exit ();) else (waitFor (function () (return page.evaluate (function () (var el \u003d document.getElementById ( "qunit-testresult"); if (el && el.innerText.match ( "completed")) (return true;) return false;)); ), function () (var failedNum \u003d page.evaluate (function () (var el \u003d document.getElementById ( "qunit-testresult"); console.log (el.innerText); try (return document.getElementsByClassName ( "fail" ). innerHTML.length;) catch (e) (return 0;) return 10000;)); phantom.exit ((parseInt (failedNum, 10)\u003e 0)? 1: 0);));)));

Для виведення в консоль повідомлень про результати в скрипт з тестами потрібно додати функцію логування.

Тепер на сайті є тестування на знання наступних тем: HTML, CSS, JavaScript, PHP, SQL.

Кожен тест складається з 10-ти питань з певної теми. Я намагався в кожному питанні зачіпати найрізноманітніші сфери застосування конкретного мови, щоб максимально ретельно перевірити Ваш рівень знань.

Безумовно, все тести безкоштовні і пройти їх може будь-хто.

Порядок проходження тесту:

  1. Переходьте за посиланням " почати тестування"У відповідного тесту.
  2. Відповідаєте на поставлені питання, вибравши єдиний правильний варіант.
  3. По завершенню тестування Ви побачите свій бал, кількість помилок, а також розбір кожного питання з тіста.

Увага! Повернутися до попереднього запитання не вийде, тому перш, ніж відповідати, думайте.

Доступні на даний момент тести

  1. HTML

    • Всього тест пройшло: 75424 людини
    • Середній бал: 2.83 з 5 балів.

    Тест на знання основ HTML. Від Вас буде потрібно знання основних HTML-тегів, А також грамотне їх використання. Так само необхідно розуміння особливостей стандарту XHTML 1.1.

  2. CSS

    • Всього тест пройшло: 32828 чоловік
    • Середній бал: 3.37 з 5 балів.

    Тест перевіряє знання з основ CSS. Для успішного проходження тесту Ви повинні знати основні види селекторів (їх синтаксис), знати основні властивості та їх можливі значення, а також знати призначення найпопулярніших псевдоелементів.

  3. JavaScript

    • Всього тест пройшло: 24845 чоловік
    • Середній бал: 3.31 з 5 балів.

    Даний тест перевіряє Ваші знання з мови JavaScript. Питання з тіста зачіпають різні сфери застосування даного мови. Дуже багато питань є на розуміння "дрібних" нюансів. В інших відношеннях він Вас потрібне знання базових речей: робота зі змінними, основні функції JavaScript, пріоритети операцій та інше.

  4. PHP

    • Всього тест пройшло: 33239 чоловік
    • Середній бал: 3.03 з 5 балів.

    Даний тест перевіряє Ваші знання з мови PHP. Від Вас вимагається знання основних конструкцій PHP, роботи зі змінними, сесій, реалізації редиректу і інших стандартних речей.
    Переконливе прохання: У тесті міститься багато питань по типу: "Що виведе скрипт?". Велике прохання, не треба копіювати його і перевіряти. Будьте чесні перед самими собою.

  5. SQL

    • Всього тест пройшло: 18014 чоловік
    • Середній бал: 3.28 з 5 балів.

    Даний тест перевіряє Ваші знання з мови запитів SQL. Питання зачіпають тільки самі базові знання цієї мови, без будь-якого заглиблення. Від Вас буде потрібно знання найголовніших SQL-запитів, а також грамотне їх використання.

Javascript - дуже популярна мова для клієнтської веб-розробки. Мова JavaScript є динамічним мовою і базується на прототіпірованії. Не дивлячись на назву, він не відноситься до мови Java, хоча і використовує схожий C-подібний синтаксис, схожі конвенції іменування.

Останнім часом Javascript почав завойовувати все більшу популярність. Він лежить в основі такої сучасної технології як AJAX. Знання javascript може стане в нагоді і для програмування на стороні сервера для гнучкого конфігурування систем.

Цільова аудиторія тесту по JavaScript

Даний тест рекомендується до здачі фахівцям, які бажають перевірити свої базові знання в області JavaScript. Також тест може застосовуватися для закріплення знань розробниками безпосередньо після ознайомлення з мовою JavaScript.

У вищих навчальних закладах даний тест буде цікавий студентам технічних спеціальностей за напрямками "Інженерія програмного забезпечення" або "Програмне забезпечення Інтернет".

Попередні вимоги до тесту javascript

Перед тим, як пройти цей тест, необхідно отримати хоча б загальне уявлення про мову Javascript.

Тест орієнтований в першу чергу на фахівців, недавно вивчили JavaScript. Тому даний тест не покриває такі складні теми як ООП в JavaScript або спеціалізоване JavaScript API.

Структура тесту з JavaScript

Питання тесту покривають такі теми:

  • основні мовні конструкції JavaScript (оголошення змінних, функцій і т.д.);
  • оператори (розгалуження, цикли);
  • роботу з рядками в JavaScript;
  • роботу з масивами в JavaScript;
  • зв'язку "JavaScript - HTML" (теги, які використовуються для інтеграції)
  • інші поняття, які не ввійшли в зазначені вище теми.

Подальший розвиток тесту по JavaScript

Наступним кроком у розвитку тесту по Javascript буде складання питань по існуючим темам тесту. Розширення переліку питань збільшить інтерес повторного проходження тесту як в цілях перевірки знань, так і з метою навчання.

І є офіційним інструментом для тестування jQuery. Але QUnit відмінно підходить для тестування будь-якого коду JavaScript і навіть здатна тестувати серверну частину JavaScript за допомогою механізмів на зразок Rhino або V8.

Якщо ви не знайомі з ідеєю "модульного тестування", не засмучуйтеся - в ній немає нічого складного для розуміння:

"модульне тестування або юніт-тестування (Англ. unit testing) - процес в програмуванні, що дозволяє перевірити на коректність окремі модулі вихідного коду програми. Ідея полягає в тому, щоб писати тести для кожної нетривіальною функції або методу. Це дозволяє досить швидко перевірити, чи не призвело чергову зміну коду до регресії, Тобто до появи помилок в уже оттестировать місцях програми, а також полегшує виявлення і усунення таких помилок. "

Визначення процитовано з Вікіпедії. Просто зробіть тести для кожного функціонального блоку вашого коду, і якщо всі тести будуть пройдені, то можна бути впевненим у відсутності помилок (головним чином залежить від того, наскільки ретельно розроблені тести).

Навіщо слід тестувати свій код

Якщо ви ніколи не писали модульних тестів раніше, то, ймовірно, просто розміщували свій код відразу на веб сервері, запускали його, стежили за проявом помилок і намагалися усунути їх у міру виявлення. Такий метод роботи народжує багато проблем.

По-перше, це дуже нудне і нудне заняття. Перевірка насправді є дуже складною роботою, тому що треба бути впевненим, що все було натиснуто. А в даному процесі є дуже велика ймовірність, що один-два моменти можуть бути пропущені.

По-друге, все, що робиться для такого тестування, не може бути використано повторно. При такому методі дуже складно знайти регресії. Що таке регресії? Уявіть, що ви написали якийсь код і протестували його, виправили всі помилки, які знайшли, і помістили код на сайті. Потім користувач надіслав відзив про нові помилки і запит на нові функції. Ви повертаєтеся до коду, виправляєте помилки і додаєте нові функції. При цьому може виникнути ситуація, коли старі помилки виявляються знову, що називається "регресією". Вам знову доводиться все перевіряти. І є шанс, що ви не знайдете свої старі помилки. У будь-якому випадку пройде час, перш ніж ви здогадаєтеся, що проблема викликана "регресією". При використанні модульного тестування ви пишіть тест. Як тільки код модифікується, ви знову фільтруєте його через тест. Eсли регресія проявляється, то якісь тести не пройдуть, і ви легко визначите, яка частина коду містить помилку. Так як ви знаєте, що змінили, то помилку буде легко виправити.

Іншою перевагою модульного тестування (особливо для веб розробок) є те, що легко протестувати кросбраузерну сумісність. Потрібно просто запустити тести в різних браузерах. Якщо будуть виявлені проблеми в браузері, то ви зможете виправити їх і запустити тест знову. У підсумку ви будете впевнені, що всі цільові браузери підтримуються, так як всі вони пройшли тестування.

Як писати тести модулів в QUnit

Отже, як же безпосередньо писати тести модулів в QUnit? Першим кроком потрібно встановити середу тестування:

Комплект для тестів QUnit

Комплект для тестів QUnit

Код, який буде тестуватися, поміщається в файл myProject.js, А тести поміщаються в myTests.js. Щоб запустити тести, потрібно просто відкрити HTML файл в браузері. Тепер прийшов час написати якийсь тест.

Будівельним блоком модульного тестування є твердження.

"Затвердження - це вираз, який прогнозує, що повертається результат при виконанні вашого коду. Якщо прогноз невірний, то твердження має значення false, Що дозволяє зробити висновки про наявність помилок. "

Для виконання тверджень їх потрібно помістити в блок тесту:

// протестуємо цю функцію function isEven (val) (return val% 2 \u003d\u003d\u003d 0;) test ( "isEven ()", function () (ok (isEven (0), "Нуль - парне число"); ok ( isEven (2), "Два - теж"); ok (isEven (-4), "І негативне чотири - теж парне число"); ok (! isEven (1), "Один - непарне число"); ok (! isEven (-7), "Як і негативне сім - непарне число");))

Тут ми визначаємо функцію isEven, Яка перевіряє парність числа, і хочемо переконатися, що ця функція не повертає помилкових значень.

Спочатку ми викликаємо функцію test (), Яка будує блок тесту. Перший параметр є рядком, яка буде виводитися в результаті. Другий параметр - поворотна функція, яка містить наші твердження. Дана поворотна функція буде викликатися один раз при виконанні QUnit.

Ми написали п'ять тверджень, все є логічними. Логічне твердження припускає, що перший параметр має значення true. Другий параметр - це повідомлення, яке виводиться в результат.

Ось що ми отримаємо після виконання тесту:

Всі наші твердження успішно підтвердилися, тому можна вважати, що функція isEven () працює так, як очікувалося.

Давайте подивимося, що трапитися, якщо твердження буде невірним.

// протестуємо цю функцію function isEven (val) (return val% 2 \u003d\u003d\u003d 0;) test ( "isEven ()", function () (ok (isEven (0), "Нуль - парне число"); ok ( isEven (2), "Два - теж"); ok (isEven (-4), "І негативне чотири - теж парне число"); ok (! isEven (1), "Один - непарне число"); ok (! isEven (-7), "Як і негативне сім - непарне число"); // Помилка ok (isEven (3), "Три - парне число");))

І ось що ми отримаємо в результаті виконання тесту:


Затвердження має помилку, яку ми допустили навмисно. Але у вашому проекті, якщо якийсь тест не проходить, а всі інші твердження правильні, то буде дуже легко виявити помилку.

інші твердження

ok () не є єдиним твердженням, яке підтримує QUnit. Існують і інші типи тверджень, які зручно використовувати при складанні тестів для ваших проектів:

затвердження порівняння

затвердження порівняння equals () передбачає, що перший параметр (який є дійсним значенням) еквівалентний другому параметру (який є очікуваним значенням). Дане твердження дуже схоже на ok (), Але виводить обидва значення - дійсне і передбачуване, що істотно полегшує налагодження коду. Так само як і ok (), equals () в якості третьої параметра може приймати повідомлення для виведення.

так замість

Test ( "assertions", function () (ok (1 \u003d\u003d 1, "один еквівалентно одному");))


Слід використовувати:

Test ( "assertions", function () (equals (1, 1, "один еквівалентно одному");))


Зверніть увагу, що в кінці рядка виводиться передбачуване значення.

А якщо значення не рівні:

Test ( "assertions", function () (equals (2, 1, "один еквівалентно одному");))


Такий запис дає більше інформації.

Затвердження порівняння використовує оператор "\u003d\u003d" для перевірки параметрів, тому воно не може працювати з масивами або об'єктами:

Test ( "test", function () (equals ((), (), "помилка, це різні об'єкти"); equals ((a: 1), (a: 1), "помилка"); equals (,, "помилка, це різні масиви"); equals (,, "помилка");))

Для таких випадків в QUnit є твердження ідентичності.

затвердження ідентичності

затвердження ідентичності same () використовує ті ж параметри, що і equals (), Але працює не тільки з примітивними типами, а й з масивами і об'єктами. Твердження з попереднього прикладу пройдуть перевірку, якщо змінити з на затвердження ідентичності:

Test ( "test", function () (same ((), (), "проходить, об'єкти мають однаковий контент"); same ((a: 1), (a: 1), "проходить"); same (, , "проходить, масиви мають однаковий контент"); same (,, "проходить");))

Зауважте, що same () використовує оператор '\u003d\u003d\u003d' для порівняння, тому його зручно використовувати для порівняння спеціальних значень:

Test ( "test", function () (equals (0, false, "true"); same (0, false, "false"); equals (, undefined, "true"); same (, undefined, " false ");))

структура тверджень

Розміщувати всі твердження в одному тесті - дуже погана ідея. Такий тест буде складно підтримувати і можна заплутатися в оцінці результатів його виконання. Тому потрібно структурувати тест, розміщуючи затвердження в окремі блоки, кожен з яких буде націлений на певну групу функцій.

Можна організовувати окремі модулі за допомогою виклику функції module:

Module ( "Модуль A"); test ( "Тест", function () ()); test ( "Ще один тест", function () ()); module ( "Модуль B"); test ( "Тест", function () ()); test ( "Ще один тест", function () ());


У попередньому прикладі всі твердження викликалися синхронно, тобто виконувалися одне за іншим. У реальному світі існує безліч асинхронних функцій, таких як запити AJAX або функції setTimeout () і setInterval (). Як нам тестувати такий тип функцій? QUnit має спеціальний тип тестів, який називається "асинхронний тест" і призначений для асинхронного тестування:

Спочатку спробуємо написати тест звичайним способом:

Test ( "Асинхронний тест", function () (setTimeout (function () (ok (true);), 100)))


Виглядає так, як ніби в тесті немає ніяких тверджень. Тому що твердження виповнилося синхронно, але до моменту виклику функції тест вже був закінчений.

Правильний варіант тестування нашого прикладу:

Test ( "Асинхронний тест", function () (// Переводимо тест в режим "пауза" stop (); setTimeout (function () (ok (true); // Після виклику затвердження // продовжуємо тест start ();), 100)))


Ми використовували функцію stop () для зупинки тесту, а після виконання затвердження знову запускали тест за допомогою функції start ().

Виклик функції stop () відразу після виклику функції test () є вельми поширеною практикою. Тому QUnit має спеціальне скорочення: asyncTest (). Попередній приклад можна переписати у вигляді:

AsyncTest ( "Асинхронний тест", function () (// Тест автоматично переводиться в режим "пауза" setTimeout (function () (ok (true); // Після виклику затвердження // продовжуємо тест start ();), 100)) )

Є один момент, над яким варто задуматися: функція setTimeout () завжди викликає свою поворотну функцію, а якщо тестувати іншу функцію (наприклад, виклик AJAX). Як бути впевненим, що поворотна функція буде викликана? Якщо поворотна функція не викликана, функція start () теж залишиться без виклику і весь тест "підвисне":


Можна організувати тест наступним чином:

// Користувацька функція function ajax (successCallback) ($ .ajax ((url: "server.php", success: successCallback));) test ( "Асинхронний тест", function () (// Зупиняємо тест і // будемо повідомляти про помилку, якщо функція start () не викликана після закінчення 1 секунди stop (1000); ajax (function () (// ... асинхронне твердження start ();))))

У функцію stop () передається значення таймаута. Тепер QUnit отримав вказівку: "якщо функція start () НЕ буде викликана після закінчення часу очікування, слід вважати даний тест проваленим ". Тепер весь тест не "підвисне" і буде видано попередження, якщо щось піде не так, як потрібно.

Тепер розглянемо випадок множинних асинхронних функцій. Де розміщувати функцію start ()? Потрібно розміщувати її в функції setTimeout ():

// Користувацька функція function ajax (successCallback) ($ .ajax ((url: "server.php", success: successCallback));) test ( "Асинхронний тест", function () (// Зупиняємо тест stop (); ajax (function () (// ... асинхронне твердження)) ajax (function () (// ... асинхронне твердження)) setTimeout (function () (start ();), 2000);))

Значення таймаута має бути достатнім для виконання викликів обох зворотних функцій перед продовженням тесту. Якщо одна з функцій не буде викликана, як визначити яка саме? Для цього є функція expect ():

// Користувацька функція function ajax (successCallback) ($ .ajax ((url: "server.php", success: successCallback));) test ( "Асинхронний тест", function () (// Зупиняємо тест stop (); / / Повідомляємо QUnit, що ми очікуємо виконання трьох тверджень expect (3); ajax (function () (ok (true);)) ajax (function () (ok (true); ok (true);)) setTimeout (function ( ) (start ();), 2000);))

Ми передаємо в функцію expect () кількість тверджень, які планується виконати. Якщо одне з тверджень не буде виконано, ви отримаєте повідомлення про те, що щось іде не так, як планується.

Є коротка запис для використання expect (): Потрібно передати кількість планованих тверджень як другий параметр test () або asyncTest ():

// Користувацька функція function ajax (successCallback) ($ .ajax ((url: "server.php", success: successCallback));) // Повідомляємо QUnit, що ми очікуємо виконання 3 тверджень test ( "asynchronous test", 3, function () (// Зупиняємо тест stop (); ajax (function () (ok (true);)) ajax (function () (ok (true); ok (true);)) setTimeout (function () (start ();), 2000);))

висновок

В даному уроці ми привели все, що потрібно для початку роботи з QUnit. Модульне тестування - чудовий метод для перевірки коду перед його використанням. Якщо ви ніколи раніше не використовували ніяких тестів, саме час почати.