Служба тестирования
Рассмотрев архитектуру и реализацию нашего приложения динамического сервис-воркера« Woz в первой части этой серии», в этом посте будет рассказано, какой подход мы приняли к написанию тестов для него.
Service Workers - мощный инструмент, но как только неисправный Service Worker установлен и активирован в браузере пользователя, все может запутаться. Более того, Service Workers, как известно, трудно отлаживать, что может вызвать головные боли и разочарование. Имея это в виду, мы знали, что нам нужно много внимания уделять нашим модульным тестам и набору функционального тестирования, чтобы иметь возможность часто и уверенно вносить изменения.
Если вы не читали первую часть этой серии, возможно, стоит вернуться и сначала прочитать ее, поскольку я сделаю ссылки на нее, которые в противном случае могут быть вам непонятны.
Аварийная кнопка
Прежде всего, мы знали, что нам нужен способ удаления неисправного Service Worker. Если вы ранее разрабатывали Service Worker для своего приложения, вы знаете, что это можно сделать с помощью инструментов разработчика браузера. Однако очевидно, что это не то, о чем вы бы хотели просить своих пользователей.
Вместо этого нам нужен был аварийный выключатель, когда мы понимали, что отправили сломанного Service Worker. Из первой части этой серии вы знаете, что мы генерируем sw.js
файл для каждого запроса к '/:language-:country/sw.js'
. Отправка sw.js
файла, содержащего только выражение console.log(“service worker has been disabled”)
, когда запрос сделан по этому маршруту, означал, что мы сможем переопределить неисправный sw.js
, активированный в браузере пользователя. Фактический выключатель, который нам нужно задействовать, - это просто поле disable: true
config в нашем динамическом приложении Service Worker Woz. Пока этот флаг установлен в true
, наше приложение будет переопределять любые существующие Service Workers.
Весь обработчик маршрута будет выглядеть примерно так:
Модульные тесты
В Woz одна единственная функция отвечает за выборку всех ресурсов, маршрутов и сторонних скриптов, которые предполагается кэшировать для данной локали. После получения этой информации создается огромная строка, которая становится фактическим файлом sw.js
:
Но как вы протестируете функцию, которая создает другую огромную строку для любой конфигурации, ресурсов и маршрутов, которые вы ей передаете? Учитывая, что шаблонные литералы позволяют интерполировать выражения, мы смогли выделить часть логики в отдельные функции, которые затем можно было встроить в шаблонный литерал с помощью выражения ${someFunction}
. В строках 10, 17 и 18 вы можете увидеть вызовы функций, которые после вызова сразу же вызываются. Как вы, возможно, уже догадались, это означает, что эти функции сами возвращают функцию, которая будет вставлена в литерал шаблона для немедленного вызова. Возьмем для примера строку 18: (${addRoutesForThirdPartyScripts(thirdPartyScripts, workbox)})();
Как следует из названия и сигнатуры функции, она принимает массив сторонних скриптов и использует API маршрутизации Workbox для кэширования этих скриптов и обслуживания запросов к URL-адресам скриптов из кеша:
Хотя мы могли бы просто добавить этот код прямо в литерал шаблона (по сути, именно так мы и начали), извлечение его в функции не только делает наш код более модульным, а функцию генерации строк - менее раздутой, но также позволяет нам объединять проверить это! 🙌
Используя имитирующую функцию Jest, мы можем проверить, правильно ли наш код определяет, доступна ли необходимая нам функция Workbox, и, если нет, пропускает все, что мы пытаемся здесь сделать, чтобы наше создание Service Worker могло продолжаться без каких-либо ошибок.
И, наконец, мы можем проверить, успешно ли зарегистрированы маршруты для наших сторонних скриптов, когда Workbox доступен:
Наши модульные тесты для других немедленно вызываемых функций в нашем литерале шаблона ((${loadWorkbox(config, logger, importScripts, workbox)})();
и (${addAppShells(config, workbox)})();
) следуют тому же шаблону.
Функциональные тесты
Приятно знать, как именно будет выглядеть sw.js
, который мы сгенерируем, и что мы можем устранить всех неисправных Service Workers, которых мы отправили нашим пользователям. Однако у нас до сих пор нет тестов, которые проверяли бы, окажет ли Service Worker желаемый эффект на нашем сайте. Чтобы проверить это, мы обратились к Puppeteer, высокоуровневому API для управления браузером Chrome или Chromium.
Мы настроили Puppeteer, используя пресет jest-puppeteer
в нашем jest.config.js
файле. Мы также добавили jest-puppeteer.config.js
файл следующего содержания:
Мы устанавливаем контекст браузера в режим инкогнито, чтобы быть уверенными, что предыдущий сервис-воркер не повлияет на новое окно. —-enable-features=NetworkService
важен, поскольку он включает Service Worker на Puppeteer (по умолчанию все еще экспериментальный). —-disable-dev-shm-usage
и —-no-sandbox
были необходимы для того, чтобы мы могли запускать кукловод в контейнере Docker как часть нашего конвейера CI. Мы установили ignoreHTTPSError: true
, чтобы преодолеть требование https для сервис-воркера. И, наконец, dumpio
нужен только для большей наглядности во время отладки.
Мы запускаем наши тесты на простом представлении, которое обслуживается Woz, тем же экспресс-сервером, который генерирует нашего Service Worker. Представление представляет собой шаблон руля, который выглядит следующим образом:
где mrp-product.css
:
#invisible { visibility: hidden; }
и mrp-product.js
:
var description = document.getElementById('description'); description.textContent = 'This demo site is designed to serve and test service worker.'; console.log('mrp-product.js has loaded');
Имея рабочий макет сайта, давайте взглянем на наши функциональные тесты. Обычно мы пишем тесты на основе одного из двух случаев: первый - когда ресурсы нашего сайта обслуживаются через сеть, а второй - когда ресурсы обслуживаются нашим сервисным работником. Первый тест, который мы проходим, предназначен для сценария, когда мы впервые посещаем нашу страницу, то есть когда наш Service Worker зарегистрирован, но еще не активирован, и когда ресурсы должны обслуживаться сетью. См. Комментарии во фрагменте кода для объяснения каждого шага:
Последний it
блок из приведенного выше фрагмента кода - это наша личная особенность среди открытий, сделанных нами при написании нашего набора функционального тестирования. Здесь мы работали с объектами PerformanceEntry (часть Performance Web API), которые косвенно создаются браузером при загрузке ресурсов, например изображения или файлы JavaScript. Используя тип PerformanceEntry resource
, который реализует интерфейс PerformanceResourceTiming, мы можем проверить, обслуживается ли актив (в данном случае mrp-product.js
и mrp-product.css
) через сеть, а не сервис-воркером. Экземпляры интерфейса PerformanceResourceTiming имеют поле workerStart
, которое представляет собой количество миллисекунд, которое потребовалось веб-работнику для обслуживания ресурса. Учитывая, что наши активы обслуживались через сеть, а не работником, он должен быть равен 0, что мы можем легко подтвердить в нашем тесте.
Вторая часть нашего теста проверяет, что происходит после перезагрузки страницы. Мы ожидаем, что Service Worker будет активирован, а активы будут обслуживаться Service Worker, а не сетью. Опять же, пожалуйста, смотрите комментарии для объяснения:
В этом случае значение workerStart
не будет равно 0, поскольку ресурсы будут обслуживаться Service Worker.
Вывод
Реализация надлежащего тестирования для нашего динамического генератора Service Worker Woz, вероятно, заняла больше времени, чем написание самого приложения. Тем не менее, мы думаем, что наличие этого окупится в долгосрочной перспективе, поскольку мы сможем разрабатывать и поставлять больше функций PWA без головной боли и разочарований и не опасаясь, что мы можем установить глючного Service Worker в браузере наших пользователей. .
Я надеюсь, что вы нашли этот пост полезным и что вы сможете использовать некоторые из наших методов тестирования в своей собственной реализации Service Worker. Если у вас есть вопросы, напишите в Twitter @MaximilianAst.
Следующая часть этой серии будет посвящена влиянию Service Worker на наши показатели производительности, которые мы надеемся опубликовать в начале 2020 года. Следите за обновлениями!