Руководство по тестированию и отладке приложений Node является выдержкой из Node.js Мэннинга в действии, второе издание . Эта книга, тщательно пересмотренная во втором издании, проведет вас через все функции, методы и концепции, необходимые для создания приложений Node производственного качества.
Приложения узла функционального тестирования
В большинстве проектов веб-разработки функциональные тесты работают, управляя браузером, а затем проверяют различные преобразования DOM в соответствии со списком пользовательских требований. Представьте, что вы создаете систему управления контентом. Функциональный тест для функции загрузки библиотеки изображений загружает изображение, проверяет его добавление и затем проверяет, добавлено ли оно в соответствующий список изображений.
Выбор инструментов для функционального тестирования Node-приложений вызывает недоумение. С высокого уровня они попадают в две широкие группы: тесты без головы и на основе браузера. Тесты без головы обычно используют что-то вроде PhantomJS для обеспечения дружественной к терминалу среды браузера, но более легкие решения используют такие библиотеки, как Cheerio и JSDOM. Браузерные тесты используют инструмент автоматизации браузера, такой как Selenium, который позволяет вам писать скрипты, которые управляют настоящим браузером. Оба подхода могут использовать одни и те же базовые инструменты тестирования Node, и вы можете использовать Mocha, Jasmine или даже Cucumber, чтобы настроить Selenium для вашего приложения.
Селен
Selenium — это популярная библиотека автоматизации браузеров на основе Java, которую можно использовать для тестирования приложений Node. С помощью драйвера для конкретного языка вы можете подключиться к серверу Selenium и запустить тесты на реальном браузере. В этой статье вы узнаете, как использовать WebdriverIO , драйвер Node Selenium.
Запустить Selenium сложнее, чем чистые тестовые библиотеки Node, потому что вам нужно установить Java и скачать файл Selenium JAR. Сначала загрузите Java для вашей операционной системы , а затем перейдите на сайт загрузки Selenium, чтобы загрузить файл JAR. Затем вы можете запустить сервер Selenium следующим образом:
java -jar selenium-server-standalone-3.4.0.jar
Обратите внимание, что ваша точная версия Selenium может отличаться. Возможно, вам также придется указать путь к двоичному файлу браузера. Например, в Windows 10 с Firefox, установленным как browserName, вы можете указать полный путь Firefox следующим образом:
java -jar -Dwebdriver.firefox.driver="C:\path\to\firefox.exe" selenium-server-standalone-3.4.0.jar
В качестве альтернативы вам может понадобиться скачать драйвер Mockilla Gecko (поместить его в ту же папку, что и исполняемый файл selenium, и запустить его так:
java -jar -Dwebdriver.gecko.driver=geckodriver selenium-server-standalone-3.4.0.jar
Точный путь зависит от того, как Firefox установлен на вашем компьютере. Подробнее о драйвере Firefox читайте в документации SeleniumHQ . Вы можете найти драйверы для Chrome и Microsoft Edge, которые настроены аналогичным образом.
Теперь, когда сервер Selenium запущен, создайте новый проект Node и установите WebdriverIO:
mkdir -p selenium/test/specs cd selenium npm init -y npm install --save-dev webdriverio npm install --save express
WebdriverIO поставляется с дружественным генератором конфигурационных файлов. Чтобы запустить его, запустите wdio config:
./node_modules/.bin/wdio config
Следуйте за вопросами и примите значения по умолчанию. Это должно выглядеть примерно так:
Обновите файл package.json с помощью команды wdio, чтобы тесты можно было запускать с помощью теста npm:
"scripts": { "test": "wdio wdio.conf.js" },
Теперь добавьте что-нибудь к тесту. Достаточно базового сервера Express. Пример используется в последующем листинге для тестирования. Сохраните этот список как index.js
.
const express = require('express'); const app = express(); const port = process.env.PORT || 4000; app.get('/', (req, res) => { res.send(` <html> <head> <title>My to-do list</title> </head> <body> <h1>Welcome to my awesome to-do list</h1> </body> </html> `); }); app.listen(port, () => { console.log('Running on port', port); });
Приведенный выше фрагмент использует ES2015. Если вы хотите освежить в этом информацию, ознакомьтесь с курсом SitePoint « Погружение в ES2015» .
Преимуществом WebdriverIO является то, что он предоставляет простой и удобный API для написания тестов Selenium. Синтаксис понятен и прост в освоении — вы даже можете написать тесты с помощью селекторов CSS. Следующий листинг (находится в test/specs/todo-test.js
) показывает простой тест, который устанавливает клиент WebdriverIO и затем проверяет заголовок на странице.
const assert = require('assert'); const webdriverio = require('webdriverio'); describe('todo tests', () => { let client; before(() => { client = webdriverio.remote(); return client.init(); }); it('todo list test', () => { return client .url('http://localhost:4000') .getTitle() .then(title => assert.equal(title, 'My to-do list')); }); });
После подключения WebdriverIO вы можете использовать экземпляр клиента для извлечения страниц из вашего приложения. Затем вы можете запросить текущее состояние документа в браузере — этот пример использует getTitle
для получения элемента title из заголовка документа. Если вы хотите запросить документ для CSS-элементов, вы можете использовать вместо него .elements . Существует несколько способов манипулирования документом, формами и даже файлами cookie.
Этот тест может запустить настоящий браузер с веб-приложением Node. Чтобы запустить его, запустите сервер на порт 4000:
PORT=4000 node index.js
Затем введите npm test
. Вы должны увидеть, что Firefox открыт и тесты выполняются в командной строке. Если вы хотите использовать Chrome, откройте wdio.conf.js и измените свойство browserName.
Более продвинутое тестирование с Selenium
Если вы используете WebdriverIO и Selenium для тестирования более сложного веб-приложения, в котором используется что-то вроде React или Angular, вам нужно проверить служебные методы. Некоторые из методов приостанавливают тест до тех пор, пока не станут доступны определенные элементы, что отлично подходит для приложений React, которые могут асинхронно отображать документ, обновляя его несколько раз в зависимости от доступности удаленных данных. Посмотрите на методы waitFor*
, такие как waitForVisible, чтобы узнать больше.
Если вы хотите узнать больше об этом виде тестирования, ознакомьтесь с Функциональным тестированием JavaScript с Nightwatch.js
Работа с ошибочными тестами
Когда вы работаете над созданным проектом, наступит момент, когда тесты начнут проваливаться. Node предоставляет несколько инструментов для получения более подробной информации о неудачных тестах. Давайте поговорим о том, как обогатить вывод, полученный при отладке неудачных тестов.
Первое, что нужно сделать, когда тесты не пройдены, — создать более подробный вывод журнала. В следующем разделе показано, как это сделать с помощью NODE_DEBUG
.
Получение более подробных логов
Когда тесты не пройдены, полезно получить информацию о том, что делает программа. У Node есть два способа сделать это: один для внутренних компонентов Node, а другой для модулей npm. Для отладки основных модулей Node используйте NODE_DEBUG .
Использование NODE_DEBUG
Чтобы увидеть, как работает NODE_DEBUG, представьте, что у вас глубоко вложенный вызов файловой системы, где вы забыли использовать обратный вызов. Например, следующий пример выдает исключение:
const fs = require('fs'); function deeplyNested() { fs.readFile('/'); } deeplyNested();
Трассировка стека показывает только ограниченное количество деталей об исключении и не включает полную информацию на сайте вызова, где возникло исключение:
fs.js:60 throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs ^ Error: EISDIR: illegal operation on a directory, read at Error (native)
Без полезного комментария многие программисты видят след, подобный этому, и обвиняют Node в бесполезной ошибке. Но, как отмечается в комментарии, NODE_DEBUG=fs
можно использовать для получения дополнительной информации о модуле fs
. Запустите скрипт так:
NODE_DEBUG=fs node node-debug-example.js
Теперь вы увидите более подробную трассировку, которая поможет отладить проблему:
fs.js:53 throw backtrace; ^ Error: EISDIR: illegal operation on a directory, read at rethrow (fs.js:48:21) at maybeCallback (fs.js:66:42) at Object.fs.readFile (fs.js:227:18) at deeplyNested (node-debug-example.js:4:6) at Object.<anonymous> (node-debug-example.js:7:1) at Module._compile (module.js:435:26) at Object.Module._extensions..js (module.js:442:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:311:12) at Function.Module.runMain (module.js:467:10)
Из этого следа видно, что проблема заключается в нашем файле внутри функции в строке 4, которая первоначально была вызвана из строки 7. Это значительно упрощает отладку любого кода, использующего основные модули, и включает в себя как файловую систему, так и сетевые библиотеки, такие как HTTP-клиент и серверные модули Node.
Использование DEBUG
Общедоступная альтернатива NODE_DEBUG
— DEBUG
. Многие пакеты на npm ищут переменную среды DEBUG
. Он имитирует стиль параметров, используемый NODE_DEBUG
, позволяя вам указать список модулей для отладки или просмотреть все из них с помощью DEBUG='*'
.
Если вы хотите включить функциональность NODE_DEBUG
в свои собственные проекты, используйте встроенный метод util.debuglog .
const debuglog = require('util').debuglog('example'); debuglog('You can only see these messages by setting NODE_DEBUG=example!');
Для создания пользовательских средств отладки, которые настроены с помощью DEBUG
, вам необходимо использовать пакет отладки из npm] (https://www.npmjs.com/package/debug). Вы можете создать столько регистраторов, сколько захотите. Представьте, что вы создаете веб-приложение MVC. Вы можете создать отдельные регистраторы для моделей, представлений и контроллеров. Затем, когда тесты не пройдены, вы сможете указать журналы отладки, необходимые для отладки определенной части приложения. Следующий листинг демонстрирует, как использовать модуль отладки.
const debugViews = require('debug')('debug-example:views'); const debugModels = require('debug')('debug-example:models'); debugViews('Example view message'); debugModels('Example model message');
Чтобы запустить этот пример и просмотреть журналы представлений, установите для DEBUG
значение debug-example:views
, например, DEBUG=debug-example:views node index.js
Последняя особенность ведения журнала отладки состоит в том, что вы можете поставить перед разделом отладки дефис, чтобы удалить его из журналов:
DEBUG='* -debug-example:views' node index.js
Скрытие определенных модулей означает, что вы все еще можете использовать подстановочный знак, но пропустите ненужные или шумные разделы в выводе.
Получение лучших следов стека
Если вы используете асинхронные операции, и это включает в себя все, что вы написали с использованием асинхронных обратных вызовов или обещаний, то вы можете столкнуться с проблемами, когда трассировки стека недостаточно подробны. Пакеты на npm могут помочь вам в таких случаях. Например, когда обратные вызовы выполняются асинхронно, Node не будет удерживать стек вызовов, когда операция была поставлена в очередь. Чтобы проверить это, создайте два файла, один из которых называется async.js
который определяет асинхронную функцию, а другой — index.js
который требует async.js
.
Этот фрагмент называется aync.js
:
module.exports = () => { setTimeout(() => { throw new Error(); }) };
И index.js
должен требовать async.js
:
require('./async.js')();
Теперь, если вы запустите index.js
с node index.js
вы получите короткую трассировку стека, в которой не отображается вызывающая index.js
с node index.js
, а только местоположение сгенерированного исключения:
throw new Error(); ^ Error at null._onTimeout (async.js:3:11) at Timer.listOnTimeout (timers.js:92:15)
Чтобы улучшить эту отчетность, установите пакет трассировки и запустите его с узлом -r trace index.js. Флаг -r
указывает Node требовать модуль трассировки перед загрузкой чего-либо еще.
Другая проблема со следами стека — они могут быть слишком подробными. Это происходит, когда трассировка содержит слишком много деталей о внутренностях Node. Чтобы очистить ваши следы стека, используйте уточнение . Опять же, вы можете запустить его с флагом -r
:
$ node -r clarify index.js throw new Error(); ^ Error at null._onTimeout (async.js:3:11)
Clarify особенно полезен, если вы хотите включить трассировку стека в электронные письма с предупреждениями об ошибках для веб-приложения.
Если вы запускаете код, предназначенный для браузеров в Node, возможно, как часть изоморфного веб-приложения, то вы можете получить более качественные трассировки стека с помощью source-map-support . Это можно запустить с -r
, но это также работает с некоторыми тестовыми средами:
node -r source-map-support/register index.js mocha --require source-map-support/register index.js
В следующий раз, когда вы будете бороться с трассировкой стека, генерируемой асинхронным кодом, ищите такие инструменты, как трассировка и уточнение, чтобы убедиться, что вы получаете лучшее из того, что могут предложить V8 и Node.
Резюме
Так что у вас есть это. В этой статье мы рассмотрели функциональное тестирование приложений Node с использованием Selenium, а также некоторые советы и рекомендации по работе с ошибочными тестами. Если вам понравилась эта статья, вы можете посетить домашнюю страницу книги и скачать бесплатную первую главу Node.js в действии, второе издание или приобрести всю книгу.