Тестування коду- Невід'ємний цикл розробки програмного забезпечення. Початківці девелоперів часто недооцінюють його роль і перевіряють працездатність програми по-старому – «працює, та й добре». Рано чи пізно ця стратегія дає збій і баг-трекер починає захльостувати незліченну армію тяган. Щоб не потрапити в таку пастку, рекомендую раз і назавжди розібратися з нюансами тестування JavaScriptкоду.

JavaScript вже не той

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

Що я маю на увазі під практиками та парадигмами? Звичайно ж, архітектурний шаблон MVC (model view controller)та патерни організації коду. Слідуючи цим не хитрим премудростям, ти зможеш писати якісніший код, який буде не тільки легко супроводжуватися, але мати здатність до автоматичного тестування.

Помилка більшості тестерів

Ні для кого не секрет, що найпопулярнішим способом тестування завжди була банальна перевірка на «око». Його суть проста до неподобства - написав кілька тисяч рядків коду, вирішив завдання і запускаєш свій витвір. Погрався, покликав – начебто все працює, можна заливати на бойовий сервер. Все гранично просто і при належній увазі розробника (в ідеалі окремої людини на прізвисько «тестер») можна покластися на коректність роботи програми.

На практиці все відбувається дещо інакше. Окремого тестувальника зазвичай немає. Розробник сам намагається перевірити працездатність програми, виконуючи певну у технічному завданні послідовність дій. Більш просунуті кузні коду автоматизують подібне інтеграційне тестування за допомогою речей на кшталт Selenium.

Таким чином, програміст отримує можливість виявити лише грубі помилки. На жаль, «тупі» і «непередбачені» дії користувача, а також хитрі ходи в бізнес-логіці, в 99% випадків залишаються за кадром.

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

Якось мені доручили розробити невелику програмку. За функціонал проект нагадував найпростішу CRM, яку я й реалізував у найкоротший термін. Отримавши належну винагороду, я передав усі вихідники замовнику і на вісім місяців забув про проект. Далі почалося найцікавіше. Замовник вирішив серйозно розширити функціонал програми та закликав мене на допомогу. Природно я взявся і почав робити нову функцію за функцією. Спочатку це було не складно, але коли дійшло справу до загальної інтеграції функціоналу, дзижчий рій багів кинувся в мій бік. Шматки коду почали конфліктувати, і доводилося витрачати багато часу на розрулювання конфліктів. «А як же ти не бачив, що з твоїм додатком проблеми?» - Запитають уважні читачі. Запускав, але через те, що додаток розрісся мені банально не вистачало часу і нервів протестувати весь функціонал скопом. Я обмежувався тестом лише окремих функцій та щедро поплатився за це. Мораль цієї байки – «Думай про тестування як невід'ємну частину розробки».

Unit тести як срібна куля

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

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

  • при передачі рядка "рядок" на виході ми отримаємо "рядок";
  • при передачі терміни "рядок 9" на виході ми отримаємо "рядок 9";
  • Ми також можемо додати тестування на інші вхідні параметри (наприклад, замінити символ пробілу на табуляцію). Загалом, чим краще ми покриємо код тестами, і передбачимо можливих негативних варіантів, тим більше шансів, що найвідповідальніший момент на голові залишиться трішки волосся.

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

    Тести! = зайвий код

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

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

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

    Не всякий код тестується

    Чому я стверджую, що замислюватись про тестування потрібно до написання основного коду? Тому, що код, який спочатку передбачається покривати unit-тестам, пишеться в дещо іншому стилі. Не всякий код можна протестувати. Код, в якому поєднується логіка та уявлення, та ще й розпханий де неможливо нормально протестувати. Тут я завжди раджу дотримуватись кількох простих правил:

  • Не слід писати великих функцій. Кожна функція повинна вирішувати одну проблему, а не 100 500 можливих ситуацій. Наприклад, не потрібно вішати код відправлення даних на сервер у функцію, що відповідає за їхню підготовку;
  • Функція, що складається з понад 10 рядків коду швидше за все погана функція;
  • Логіка та уявлення в жодному разі не повинні бути разом;
  • QUnit – класика жанру від творців jQuery

    QUnitкористується особливою популярністю серед розробників JavaScript. По-перше, вона добре документована і проста у використанні, а по-друге вона створена авторами jQuery. Бібліотека підходить як для тестування коду, створеного на базі jQuery, так і JavaScript.

    Завантажити останню версію QUnit ти можеш із офіційного сайту - http://qunitjs.com/. Бібліотека постачається у вигляді одного JS та CSS файлу. Припустимо, що із завантаженням необхідних компонентів ти розібрався, а раз так, то саме час написати пробний тест. Не будемо далеко ходити і спробуємо протестувати вищезгадану функцію trim().

    Для демонстрації тестів я створив найпростіший проект із наступною структорою:

    Index.html – основний файл, який відображатиме результати тестів; - qunit-1.12.0.js – файл бібліотеки qunit; - example.js – файл, що містить код для тестування (у разі опис функції trim()); - test.js – файл із тестами; - qunit-1.12.0.css – стилі для оформлення звіту із тестами;

    Вміст файлу index.html і test.js представлений у лістингу 1 і 2. Найбільше нас цікавить другий лістинг, в якому наведено оголошення функції, що тестується (trim()) і код тестів для перевірки її працездатності. Зверніть увагу, сама функція trim() може розташовуватися будь-де, я її засунув у другий лістинг тільки задля економії місця в журналі.

    Тепер подивимося самі тести. Для здійснення перевірок працездатності нашого коду Qunit.jsпропонує нам низку методів:

  • test()- Обгортка для опису тесту;
  • ok()- Затвердження дозволяє перевірити істинність першого параметра. У нашому прикладі я передаю їй виклик певної функції trim() і порівнянню зі значенням, яке я чекаю отримати. Якщо умова істинна – тест пройдено;
  • equal()– метод дозволяє перевірити рівність першого та другого параметра. Відразу зверніть увагу, що даний метод виконує нестрогу перевірку, тому годиться тільки для скалярних величин;
  • notEqual()- Протилежний equal (). Виконується, якщо перше значення, не дорівнює другому;
  • strictEqual()–аналогічний equal() з однією лише відмінністю – він використовує строгу перевірку (тобто. перевіряє ще й тип даних);
  • notStrictEqual()– метод протилежний strictEqual();
  • deepEqual()– метод для рекурсивних тверджень, що застосовується для примітивів, масивів, об'єктів;
  • notDeepEqual()– метод протилежний deepEqual();
  • raises()- Затвердження для тестування функцій зворотного виклику, що генерують виняток;
  • У другому лістингу я наочно показав, як застосовувати ці методи практично. Якщо запустити тестовий приклад у такому вигляді, всі тести будуть успішно пройдені (див. відповідний малюнок). Щоб побачити різницю між успішно пройденими тестами і завершимося з помилками, я трохи змінив код одного тесту. У рядок із тестом за допомогою strictEqual()я додав помилковий результат (див. відповідний малюнок).

    Лістинг 1. Вміст файлу index.html

    Тестування за допомогою QUnit

    Лістинг 2. Файли тестів та функція trim()

    function trim(string) ( return (string || "").replace(/^\s+|\s+$/g, ""); ) test("Тест функції trim()", function() ( ok(trim ("test") == "test", "обрізаємо крайні прогалини"); ok(trim("1") == "1", "дуже багато пробілів з боків"); = "24", "пробіли та таби з боків"); equal(trim(""), "", "Порожній рядок");

    З тестуванням простих функцій начебто розібралися. Принаймні мені додати більше нічого. Далі треба брати реальний код та пробувати писати тести самостійно. Подивимося на інше, часто виникає завдання перед JavaScript-Розробниками - тестування асинхронних функцій. Програма, напхана JavaScript-кодом, в 99% взаємодіє з серверною частиною за допомогою Ajax. Залишати цей код без перевірки також не можна, але написання тестів виглядатиме трохи інакше. Розглянемо приклад:

    AsyncTest("myAsyncFunc()", function() ( setTimeout(function() ( ok(myAsyncFunc() == true, "Дані успішно передані"); start(); ), 500); ));

    Головна відмінність цього прикладу від попереднього – замість обгортки test() застосовується asyncTest(), тим самим заявляючи, що мене цікавить тестування саме асинхронне тестування. Далі я запускаю час очікування 500 мл. сек. За цей час функція myAsyncFunc() повинна передати дані на тестовий сервер, і якщо все повернути true. Ось тут настає найцікавіший момент. Коли відбувається виклик asyncTest(), потік виконання зупиняється і після закінчення тесту його необхідно самостійно запустити. Для управління потоком виконання QUnitє методи start() та stop().

    Тестування асинхронних функцій за допомогою бібліотеки QUnitвиконується досить легко. Останній приклад, який мені хотілося б розібрати, пов'язаний із написанням тесту, який виконує кілька асинхронних перевірок. Головне питання, яке виникає на цьому у подібних завданнях, – оптимальне місце для старту потоку виконання. Офіційний док пропонує застосовувати у таких випадках щось на зразок:

    AsyncTest("myAsyncFunc()", function() ( expect(3); //Тут робимо три перевірки ok(myAsyncFunc(), "Робимо світ краще 1"); ok(myAsyncFunc(), "Робимо світ краще 2")) ok(myAsyncFunc(), "Робимо світ краще 3"); setTimeout(function() ( start(); ), 3000); ));

    Тест для дій користувача

    Завжди треба пам'ятати, що JavaScript пишеться дуже багато всяких інтерфейсних штук. Наприклад, користувач кликає по пімпі і у відповідь на його клік має щось статися. Подібного «інтерфейсного» коду в проектах дуже багато і його також потрібно покривати тестами. Давай подивимося, як можна змоделювати натискання клавіші і написати для цього дії окремий тест. Уявімо, що ми маємо якусь функцію, яка логує натиснені клавіші. Її код я навів у третьому лістингу:

    Лістинг 3. Логування натиснутих клавіш

    function KeyLogger(target) ( if (!(this instanceof KeyLogger)) ( return new KeyLogger(target); ) this.target = target; this.log = ; var self = this; this.target.off("keydown") .on("keydown", function(event) ( self.log.push(event.keyCode); )); )

    Тепер спробуємо цю функцію протестувати. Насамперед, у тілі тесту нам необхідно емулювати натиснуту клавішу. Найпростіше це зробити за допомогою бібліотеки jQuery, яка дозволяє створити подію в кілька рядків коду (див. листинг 4).

    Лістинг 4. Код тесту для KeyLogger

    test("Тест запису клавіш", function() ( var event, $doc = $(document), keys = KeyLogger($doc); event = $.Event("keydown"); event.keyCode = 9; $doc .trigger(event);equal(keys.log.length, 1, "Клавіша записана");

    На початку листингу з тестом я готую подію для емуляції натискання клавіші – «keydown». Нас цікавитиме натискання клавіші Tab (код 9). Потім за допомогою методу trigger() я відправляю приготовану подію, після чого можна приступати до тестування. Спочатку перевіряємо загальну картину – чи була натиснута клавіша, а потім її код.

    DOM під прикриттям тестів

    Раз Qunit.jsдозволяє тестувати дії користувача, то з написанням тестів для DOM теж не повинно бути проблем. Це справді так і наведений приклад нижче підтвердить мої слова. Я не коментуватиму його, просто поглянь на код і все стане зрозумілим:

    Test("Додаємо новий елемент div", function() ( var $fixture = $("#qunit-fixture"); $fixture.append("

    Це новий див
    "); equal($("div", $fixture).length, 1, "Новий div успішно доданий!"); ));

    Phantom.JS – запускаємо тести з консолі

    Писати тести за допомогою бібліотеки Qunit.jsзручно і просто, але рано чи пізно її тебе відвідає бажання якось автоматизувати запуск тестування та збирання результатів. Наприклад, у мене для цієї справи є окрема віртуальна машина DigitalOcean, керувати якою я можу лише за допомогою консолі.

    Досить елегантно цю проблему дозволяє вирішити проект phantom.js. Це не черговий фреймворк для написання Unit-тестів, а повноцінна консольна версія движка WebKit. Якщо сказати простіше, то ця програма емулює браузер. За допомогою phantom.js реально не просто автоматизувати перевірку виконання тестів, але й вирішити безліч завдань, які рано чи пізно виникають перед розробником: отримання результатів рендінгу сторінок у файл (png, jpg), функції мережевого монітора (швидкість завантаження, загальна продуктивність тощо). д.), емуляція дій користувача тощо. Рекомендую не полінуватися і почитати офіційну документацію щодо цього проекту, обов'язково знайдеш щось цікаве для себе.

    Phantom.jsможна зібрати під різні платформи (nix, mac OS X, windows). Якщо ти все розробляєш під Windows, то немає жодних проблем – зливай бінарники й уперед. Невеликі проблеми із запуском можуть виникнути, якщо в тебе встановлено два відеоадаптери, один з яких NVidia. В цьому випадку тобі доведеться скористатися хаком, описаним у врізанні.

    Спробуємо познайомитись з phantom.js на практиці. Щоб пропустити через phantom.jsтести, підготовлені в минулому розділі та отримати результати виконання в консоль, нам знадобиться спеціальний сценарій-лоадер – run-qunit.js . Відкриваємо консоль (я працюю у Windows, тому використовую cmd) та набиваємо команду у форматі:

    Phantom.exe<путь к run-qunit.js> <путь к странице с тестами>

    У моєму випадку команда запуску вийшла такою:

    E:\soft\phantomjs>phantomjs.exe E:\temp\testjsforx\qunit\run-qunit.js file:///E: /temp/testjsforx/qunit/index.html Результат її виконання: Tests completed in 2592 milliseconds . 9 оцінок з 9 пройшли, 0 failed.

    All tests passed

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

    Коли на тести немає часу

    За відсутності часу немає сенсу писати тести для простих функцій (взяти той же trim() з прикладів у статті), краще зосередити на найбільш критичних ділянках коду. Дотримуватися цього правила слід при написанні коду, що часто змінюється. Технічне завдання живого проекту часто змінюється, і деякі функції доводиться постійно оновлювати. Такі зміни можуть спричинити неприємні моменти – з новими даними змінений код працює добре, а старі органічно не перетравлює. Ось щоб не зловити тут фейл, подібні функції краще одразу покрити тестами. Запам'ятай просте правило - немає часу покрити весь код тестами, покрий найважливішу його частину.

    Правила добрих тестів

  • Тест має бути максимально простим. Чим складніший тест, тим більша ймовірність припуститися в ньому помилок;
  • Тести необхідно групувати на модулі, щоб потім було простіше знайти помилки та мати можливість тестувати певні частини програми;
  • Кожен тест ні залежати від інших тестів;
  • Завжди пиши окремий тест при кожному виявленні багів;
  • Проблеми phantom.js у Windows

    Так уже вийшло, але всі приклади до цієї статті я тестував не в Linux, а під старим-добрим Windows 7. Виявляється, phantom.js має невеликі проблеми при роботі на системах, в яких використовується кілька відеоадаптерів. На моєму ноутбуці, крім інтегрованого відео чіпа, ще тусується NVidia і через phantom.js категорично відмовлявся реагувати на команду phantom.exit(). В результаті після виконання сценарію процес phantom.js не завершував свою роботу і продовжував висіти в пам'яті. Вікно терміналу також припиняло реагувати на команди завершення (ctrl + c – не допомагав).

    Якщо ти зіткнувся з подібною проблемою і плануєш використати phantom.jsна Windows, то приготуйся зробити наступний хак. Відкрий панель керування Nvidia. Знайди в дереві пункт "Параметри 3D". Праворуч повинна з'явитися опція «Переважний графічний адаптер». За замовчуванням її значення встановлено у «Автовиборі». Нам треба її поміняти на Високопродуктивний процесор Nvidia або Інтегроване графічне обладнання. Після цього нехитрого трюку phantom.jsпочав поводитися слухняно.

  • Cristian Johansen Test-Driven JavaScript Development – ​​одна з небагатьох книг, що розглядають JavaScript з точки зору написання тестів;
  • Джон Резінг, Беер Бібо «Секрети JavaScript ніндзя» – гарна книга, яка стане в нагоді в першу чергу JS розробникам із середнім рівнем підготовки. У книзі детально розглядаються питання написання ефективного крос-браузерного коду, нюанси обробки подій та багато інших смакот.
  • Якось мій друг висловив своє здивування з приводу того, як взагалі мова JavaScript може використовуватися для написання серйозних корпоративних продуктів, адже він не має компілятора. Насправді вирішальну роль при створенні якісного коду відіграє аж ніяк не факт наявності компілятора для мови програмування, а правильно обраний та добре налаштований технічний процес створення програмної системи.

    Цей процес має включати комплекс засобів контролю якості та ефективності роботи програміста. Такими засобами можуть бути: модульне та інтеграційне тестування, безперервна інтеграція (Continuous Integration, CI), збирання та аналіз різноманітних метрик (наприклад, дуже довгі методи в nDepend), перевірка на відповідність вимогам JsLint, FxCop та ін.

    У цій статті я хочу розповісти, як правильно виконувати автоматичне модульне та інтеграційне тестування вашого продукту мовою JavaScript. Насправді в цьому плані мова JavaScript нічим кардинально не відрізняється від Java або C#.

    Agile, TDD та BDD

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

    Існують також методики програмування, які вимагають починати кодування логіки з написання модульного тесту: Test-Driven Development (TDD) та Behavior-Driven Development (BDD). Вони часто використовуються в Agile-процесі. Розглянемо їх особливості докладніше.

    Test-Driven Development

    Розробка через тестування - це ітеративний процес написання коду, в якому повторюються наступні чотири кроки:

    Крок 1. Перш ніж додати новий фрагмент логіки, створіть модульний тест для перевірки цієї логіки;

    Крок 2. Запустіть тест і переконайтеся, що він непроходить;

    Крок 3. Напишіть найпростіший код, який змусить тест виконатись успішно;

    Крок 4. Відредагуйте код відповідно до вимог якості, заберіть дублювання коду і переконайтеся, що тест проходить успішно.

    Під модульним тестом розуміється код, який тестує роботу деякого компонента (модуля) в ізольованому середовищі. Під інтеграційним тестом розуміється код, який тестує спільну роботу кількох компонентів. Щоб протестувати модуль в ізольованому середовищі у разі, коли він залежить від інших модулів, використовуються «дублери» (test doubles).

    Test Doubles

    Розподіл допоміжних об'єктів, що використовуються при модульному тестуванні, на категорії бере свій початок із книги xUnit Test Patterns Жерара Мезароса (Gerard Meszaros). Ці категорії узагальнено називаються "тестові дублери" (test doubles). Дублери бувають наступних видів:

    • Fake;
    • Dummy.

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

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

    Spy— це допоміжний об'єкт для інспектування викликуваних методів і параметрів, що передаються їм, в ході тесту.

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

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

    Різниця між Stub та Mock полягає у способі перевірки результатів роботи тесту. У разі Stub в кінці тесту перевіряється стан об'єкта. У випадку з Mock під час тесту перевіряється те, що об'єкт використовується саме так, як було описано під час реєстрації. Подробиці можна дізнатися з нотатки Mocks Aren't Stubs Мартіна Фаулера (Martin Fowler), а я наведу тут лише приклад.

    Stub Mock
    "test connect should start polling": function () ( this.client.url = "/my/url"; sinon.stub(ajax, "poll").returns(()); this.client.connect(); sinon.assert.calledWith(ajax.poll, "/my/url"); ) "test connect should start polling": function () ( this.client.url = "/my/url"; var mock = sinon.mock(ajax) mock.expects("poll") .withArgs("/my/url ") .returns(()); this.client.connect(); mock.verify(); )

    Behavior-Driven Development

    Ітераційний підхід до розробки програмного забезпечення через функціональні вимоги — це вже знайомий нам стиль розробки через тестування, орієнтований на результат. У процесі BDD послідовно виконуються такі три кроки:

    Крок 1. Визначення функціональних вимог до модуля, що реалізується, у вигляді тестів;

    Крок 2. кодування модуля;

    Крок 3. Перевірте, що всі побажання замовника або бізнес-аналітика () виконані шляхом перевірки результатів запуску тестів.

    При написанні тестів у стилі BDD дуже зручно використовувати Mock-об'єкти через те, що вони чудово відображають вимоги щодо функціональної частини для компонента. Таким чином, тести в процесі BDD можуть бути формалізованим поданням завдання ( user story) в термінах Scrumщо дозволяє економити час на написанні технічного завдання та документації по готовому продукту.

    Яким має бути фреймворк для модульного тестування JavaScript?

    Повноцінний інструмент для модульного та інтеграційного тестування JavaScript повинен складатися з наступних компонентів:

    • Assertion library (набір методів для перевірки стану компонента наприкінці кожного тесту);
    • Mock library (інструмент для генерації Mock-об'єктів та інших "дублерів");
    • Test runner (інструмент автоматичного запуску тестів із підтримкою більшості браузерів, включаючи браузери iOS та Android);
    • Блок підключення до популярних систем безперервної інтеграції (Continuous Integration).

    Стратегії модульного тестування коду мовою JavaScript

    Сьогодні існує три стратегії модульного тестування JavaScript коду (докладніше - у третьому розділі книги Test-Driven JavaScript Development Крістіана Йохансена (Christian Johansen)):

    • In-Browserтестування;
    • Headlessтестування;
    • Тестування по дорозі JsTestDriver.

    In-Browser тестування передбачає запуск всіх модульних та інтеграційних тестів з HTML-сторінки, яку розробник відкриває у потрібних браузерах самостійно. Такий підхід простий та інтуїтивно зрозумілий. Однак його мінусом є те, що він не передбачає можливості включення подібних тестів до Continuous Integration. Крім того, запускати вручну HTML-сторінку в десяти і більше браузерах і постійно натискати "F5" може бути стомлюючим для розробника.

    Headless тестування полягає в тому, що весь JavaScript-код тестується на емуляторі, який може бути написаний на Java, Ruby, JavaScript, C++ і т.д. Найвідомішим емулятором на сьогоднішній день є PhantomJS, який є двигуном WebKit, що запускається з командного рядка. З переваг емулятора можна відзначити те, що його можна використовувати в Continuous Integration, а також те, що він дозволяє автоматизувати запуск всіх тестів з командного рядка. Однак такий підхід має істотний недолік — код не тестується на реальних браузерах, тому є ризик пропустити помилки браузера, які не відтворюються на емуляторі. До появи JsTestDriver можна було часто зустріти те, що In-Browser тестування комбінується з Headless тестування, оскільки вони чудово доповнюють один одного.

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

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

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

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

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

    Що тестувати

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

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

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

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

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

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

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

    Як тестувати

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

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

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

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

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

    (function() ( window.stepen = function(int) ( var result = 2; for (var i = 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) === 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()– вважає тест успішним, якщо результат, що повертається = 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"); "); html(data); return data; )) ), "Template loading check");

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

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

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

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

    /** * Відсутність тесту умови є true або timeout occurs. * Useful for waiting * on a server response або for ui change (fadeIn, etc.) to occur. * * @param testFx javascript condition that evaluates to a boolean, * it can be passed in as string (e.g.: "1 == 1" or * "$("#bar").is(":visible")" or * as a callback function. (":visible")" or * as a callback function. * @param timeOutMillis max amount of time to wait. If not * specified, 3 sec is used. = 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 = новий WebPage(); // Route "console.log()" calls from in the Page // context to the main Phantom context (i.e. current "this") page.onConsoleMessage = function(msg) ( console.log(msg); ); page.open(phantom.args, function(status)( if (status !== "success") ( console.log("Unable to access network"); phantom.exit(); ) else ( waitFor(function() ( return page.evaluate(function()( var el = document.getElementById("qunit-testresult"); if (el && el.innerText.match("completed")) ( return true; ) return false; )); ), function()( var failedNum = page.evaluate(function()( var el = 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) > 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-запитів, а також грамотне їх використання.

    На прикладі простого калькулятора на 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 = require ("assert"); it("має повертати true", () => ( assert.equal(true, true); ));

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

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

    Тест пройшов як і очікувалося, тому з налаштуванням середовища покінчено. Видаліть із test.jsвсе, крім рядка const assert = 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 = require ("assert"); it("правильно знаходить суму 1 і 3", () => ( assert.equal(add(1, 3), 4); ));

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

    > 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. Подивіться на more details.

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

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

    Const add = (x, y) => (+x) + (+y);

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

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

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

    Const add = (x, y) => (+x) + (+y); module.exports = (add)

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

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

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

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

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

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

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

    Const add = (x, y) => (+x) + (+y); const subtract = (x, y) => (+x) - (+y); const multiply = (x, y) => (+x) * (+y); const divide = (x, y) => (+x) / (+y); module.exports = ( add, subtract, multiply, divide, )

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

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

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

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

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

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

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

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

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

    Const validateNumbers = (x, y) => ( if (isNaN(x) && isNaN(y)) ( return false; ) return true; )

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

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

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

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

    Const validateNumbers = (x, y) => ( if (isNaN(x) || isNaN(y)) ( return false; ) return true; )

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

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

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

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

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

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

    Const operations = require("./operations.js");

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

    Const readline = require ("readline");

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

    Const rl = 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) => ( rl.question("Введіть друге число: ", (y) => ( rl.question(` Виберіть одну з наступних операцій) Додавання (+) Віднімання) (-) Множення (*) Поділ (/) Ваш вибір: `, (choice) => ( // тут ще з'явиться код 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 "4": console.log(`Приватне $(x) і $(y) дорівнює $(operations.divide(x, y)).`); break; перезапустіть програму та виберіть число від 1 до 4."); break; ) )

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

    /** * Простий калькулятор на Node.js, який використовує calculator app that uses * вбудований інтерфейс командного рядка Readline. */ const operations = require("./operations.js"); const readline = require ("readline"); // Використовуємо readline для створення інтерфейсу const rl = readline.createInterface((input: process.stdin, output: process.stdout)); console.log(` Calc.js Ви відкрили калькулятор на Node.js! Версія: 1.0.0. Використання: користувач повинен ввести два числа, а потім вибрати, що з ними зробити. `); rl.question("Введіть перше число: ", (x) => ( rl.question("Введіть друге число: ", (y) => ( rl.question(` Виберіть одну з наступних операцій) Додавання (+) Віднімання) (-) Розмноження (*) Поділ (/) Ваш вибір: `, (choice) => ( 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)).`); x) і $(y) одно $(operations.multiply(x, y)).`), break; divide(x, y)).`); break; ;));

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

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

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