Share This
Связаться со мной
Крути в низ
Categories
//Webpack на практике: с нуля до создания автотестов

Webpack на практике: с нуля до создания автотестов

Перевод статьи Webpack: From 0 to automated testing

Язык JavaScript повсеместно используется для создания больших веб-сервисов. Для таких проектов приходится импортировать много стороннего кода (Lodash, React, Angular и др.). Из-за этого код усложняется, и в нём гораздо чаще возникают ошибки. Чем больше в вашем коде будет зависимостей, тем большей головной болью станет подключение тегов <script> в правильном порядке.

Webpack создаёт граф зависимостей для JavaScript, CSS и прочих, выдавая однофайловые сборки кода так, чтобы вы могли импортировать все необходимые ресурсы JavaScript всего одним тегом <script>.

Это руководство поможет создать сборку для небольшого веб-приложения, а затем научит вас использовать Webpack для настройки автоматических тестов.

Создание приложения

В качестве тестового приложения мы сделаем карту для ленивцев, которая поможет найти магазины в Кембридже, где продают травяной чай из гибискуса. Потому что каждый ленивец в кембриджском заповеднике Fresh Pond знает, что чай из гибискуса — лучший чай, чтобы умерить свой горячий темперамент!

webpack na praktike s nulja do sozdanija avtotestov 45a199a - Webpack на практике: с нуля до создания автотестов

Прим.: на самом деле в заповеднике Fresh Pond не обитают ленивцы, но они правда любят вкусные цветы гибискуса после долгого дня, проведённого на деревьях.

Создайте каталог с именем webpack-mocha-tutorial, в него добавьте другой каталог app/src и запустите пакетный менеджер npm init или yarn init. Исходный код приложения находится здесь. Также в статье будут ссылки на коммиты, чтобы прослеживать изменения кода по ходу чтения.

Основная структура приложения будет выглядеть так:

  • У вас есть файл app/src/distance.js, экспортирующий функцию, которая запускает формулу вычисления расстояния (на самом деле нужно использовать ортодому) и функцию, которая сообщает, какая точка из массива точек ближе всего находится.
// Функция distance() принимает 2 значения, представленных // числами x и y, и возвращает расстояние между ними // // [TODO] Используйте ортодому function distance(p2, p1) {  let yDist = p2.y - p1.y;  let xDist = p2.x - p1.x;  return Math.sqrt(Math.pow(yDist, 2) + Math.pow(xDist, 2)); } // sortByDistance принимает ваше местоположение и массив точек // и возвращает отсортированный массив точек function sortByDistance(myPt, points) {  return points.sort(    (pt1, pt2) => distance(pt1, myPt) - distance(pt2, myPt)); } 
  • Также у вас есть файл app/src/page.js, который использует код из distance.js, чтобы вывести ближайший магазин из списка, а затем отобразить его на странице.
let stores = [  {name: "Cambridge Naturals",     x: -71.1189, y: 42.3895},  {name: "Sarah's Market",         x: -71.1311, y: 42.3823},  {name: "Whole Foods Fresh Pond", x: -71.1420, y: 42.3904}, ]; let here = {name: "You are here",  x: -71.1470, y: 42.3834}; let nearest = sortByDistance(here, stores)[0]; document.getElementById("nearest-store").innerHTML = nearest.name;
  • И, наконец, у вас есть страница index.html.
<!DOCTYPE html> <html>  <head>    <title>Ближайший магазин чая из гибискуса</title>  </head>  <body>    <p>Nearest store is <span id="nearest-store"></span></p>    <script src="app/src/distance.js"></script>    <script src="app/src/page.js"></script>  </body> </html>

Общая структура каталогов такова:

webpack na praktike s nulja do sozdanija avtotestov 7075349 - Webpack на практике: с нуля до создания автотестов

Таким образом, файл distance.js определяет функции расстояния, затем файл page.js запускает их, помещая результат функции sortByDistance() в дерево документов (DOM). Но если посмотреть на зависимость между файлами, то видно, что файл page.js зависит от файла distance.js, а не наоборот (Commit 2).

Добавляем Webpack

Для работы с Webpack необходимо его установить с помощью npm или yarn:

$ yarn add --dev webpack webpack-cli

Теперь у вас подключён Webpack и доступна его командная строка. Но прежде, чем можно будет запустить сборку, файл page.js должен импортировать код из distance.js. А distance.js экспортирует свои функции с помощью строки:

module.exports = {distance, sortByDistance}; 

И чтобы page.js мог использовать экспортированную функцию sortByDistance(),  добавляем строку:

import {sortByDistance} from "./distance";

Отлично, теперь все зависимости JavaScript связаны. Используем Webpack для создания приложения. Выполним следующую команду:

$ npx webpack app/src/page.js

Сейчас вы должны увидеть новый файл dist/main.js, который содержит весь ваш код из page.js и distance.js. Далее получаем index.html с импортом dist/main.js вместо всех скриптов из app/src, изменив код страницы следующим образом:

<!DOCTYPE html> <html>  <head>    <title>Ближайший магазин чая из гибискуса</title>  </head>  <body>    <p>Ближайший магазин: <span id="nearest-store"></span></p>    <script src="dist/main.js"></script>  </body> </html>

Теперь можете открыть страницу в браузере, код по-прежнему должен работать. Поскольку файл main.js содержит весь код из distance.js и page.js, можно импортировать всё из одного файла.

Frontend developer (Vue)

Sportmaster Lab, Липецк, до 130 000 ₽

tproger.ru Вакансии на tproger.ru

Это работает так: с помощью команды $ npx webpack app/src/page.js вы указываете, что отправной точкой (в терминологии Webpack — точкой входа в ваш код JavaScript) является page.js. Webpack читает файл page.js и видит в нём строку import {sortByDistance} from ./distance. Теперь он знает, что distance.js является зависимостью к page.js. И из всех зависимостей в вашем коде Webpack строит граф и использует его для построения пакетного JavaScript-файла dist/main.js (Commit 3).

webpack na praktike s nulja do sozdanija avtotestov b1d25e5 - Webpack на практике: с нуля до создания автотестов

Webpack строит граф зависимостей из точки входа, page.js

Между прочим, это также работает, когда ваш код импортирует сторонние зависимости в каталог node_modules

Прим. перев.: все модули, используемые в npm, по умолчанию подключаются из директории node_modules.

Давайте попробуем выполнить некоторые манипуляции с графом зависимостей с помощью jQuery вместо document.getElementById(). Сначала установим jQuery:

$ yarn add --dev jquery

Затем обновим page.js, чтобы включить в него jQuery:

import {sortByDistance} from "./distance"; import $ from "jQuery"; let stores = [  {name: "Cambridge Naturals",     x: -71.1189, y: 42.3895},  {name: "Sarah's Market",         x: -71.1311, y: 42.3823},  {name: "Whole Foods Fresh Pond", x: -71.1420, y: 42.3904}, ]; let here = {name: "You are here",  x: -71.1470, y: 42.3834}; let nearest = sortByDistance(here, stores)[0]; $("#nearest-store").html(nearest.name); 

Теперь граф зависимостей выглядит так:

webpack na praktike s nulja do sozdanija avtotestov b3108e9 - Webpack на практике: с нуля до создания автотестов

Ваш новый граф зависимостей, где page.js включает jQuery

И если выполнить $ npx webpack app/src/page.js и перезагрузить index.html (несмотря на то, что размер файла dist/main.js намного больше из-за кода jQuery) приложение по-прежнему работает.

Прежде чем продолжить, перейдите в файл package.json и добавьте эти три строчки:

"scripts": {  "build": "webpack app/src/page.js" } 

Теперь, чтобы запустить сборку пакета, можно просто выполнить $ yarn build вместо $ npx webpack app/src/page.js. Если команда для сборки изменится, будет удобнее просто обновить её в файле package.json с помощью новой команды сборки и вы по-прежнему можете выполнять сборку с помощью $ yarn build, вместо того чтобы привыкать к запуску новой команды (Commit 4).

Настройка Webpack с помощью файла webpack.config.js

То, что можно сделать с помощью команды $ npx webpack app/src/page.js, является стандартным режимом работы Webpack. Если выполнить $ webpack [entry-file.js], то он создаст граф зависимостей из входного файла и выдаст пакетный файл dist/main.js. Но задать расположение точек входа и выхода программы можно, настроив файл конфигурации. Поместите следующий код в файл с именем webpack.config.js:

module.exports = {  entry: __dirname + "/app/src/page.js",  output: {    path: __dirname + "/dist/",  } }

Теперь можно выполнить $ npx webpack или сделать ту же сборку, что и раньше, без указания точки входа в программу в аргументах командной строки, т. к. теперь всё это есть в webpack.config.js. Также это значит, что нужно обновить скрипт файла package.json следующим образом:

"build": "webpack",

Если в файле конфигурации изменить путь вывода на что-то вроде  __dirname + "/somewhere_else", то при повторном выполнении команды $ yarn build пакетный файл будет помещён в somewhere_else/main.js (Commit 5).

Файл конфигурации предназначен не только для настройки расположения входных и выходных файлов. Также можно настроить что именно Webpack делает, когда встречает файлы разных типов, используя специальные загрузчики, которые на самом деле являются JavaScript-программами, преобразующими ваш код. Например, в файле конфигурации может быть правило, определяющее, что, если Webpack встречает файл TypeScript в своём графе зависимостей, этот файл отправляется в загрузчик, который преобразует его из TypeScript в обычный JavaScript.

Загрузчик, который будет использоваться далее, — это Babel. Если вы не использовали его раньше, Babel — это инструмент, который берёт код JS, использующий современные функции, и преобразует его в эквивалент, совместимый со старыми версиями JavaScript. Это позволяет вашему приложению работать в старых браузерах или в браузерах, которые ещё не поддерживают некоторые новые функции JavaScript. В конце концов, некоторые ленивцы не обновляли свои браузеры с 2009 года. Некоторая часть написанного кода не будет работать в браузере 2009 года:

return points.sort((pt1, pt2) =>  distance(pt1, myPt) — distance(pt2, myPt));

Здесь используется стрелочная функция и старые браузеры её не воспринимают. Поэтому используем загрузчик и отправим эту функцию в прошлое. Для начала выполним следующее:

$ yarn add --dev babel-core babel-loader@7.1.5 babel-preset-env

Затем в файле webpack.config.js добавим следующий код в module.exports:

module: {  rules: [    {      test: /.js$/,      exclude: ["/node_modules/"],      use: [        {          loader: "babel-loader",          options: {            presets: ["env"],          },        },      ],    },  ], },

Этот код добавит новое правило в ваш Webpack. Если в дереве зависимостей Webpack встречает файл, который заканчивается на .js (например, distance.js), и этот файл отсутствует в папке node_modules (например, jQuery), то к этому файлу применяется данное правило.

Любой файл, который соответствует этому правилу, проходит через все загрузчики в блоке use (в данном случае только через загрузчик babel). Таким образом, файлы distance.js и page.js пройдут через загрузчик, что приведёт к удалению стрелочной функции в distance.js, а затем Webpack продолжит свой путь сборки. Тем временем, как только Webpack встречает jQuery, он просто загружает этот код как он есть без загрузчиков, поскольку jQuery находится в каталоге node_modules.

webpack na praktike s nulja do sozdanija avtotestov f3967fc - Webpack на практике: с нуля до создания автотестов

Теперь, если выполнить $ yarn build и посмотреть исходный код в dist/main.js, фрагмент, соответствующий вашей функции сортировки, использует ключевое слово function, а не стрелочную функцию (Commit 6).

webpack na praktike s nulja do sozdanija avtotestov 256f504 - Webpack на практике: с нуля до создания автотестов

webpack na praktike s nulja do sozdanija avtotestov 8511359 - Webpack на практике: с нуля до создания автотестов

Подсвеченный код на верхнем изображении — это функция sortByDistance() из dist/main.js до использования загрузчика, а подсвеченный код внизу — та же функция после добавления загрузчика. Обратите внимание на то, как выше мы используем стрелочную функцию, а ниже присутствует уже знакомое браузеру 2009 года ключевое слово function.

Теперь приложение готово к работе на устаревших браузерах. Чтобы этот код был поддерживаемым, следует написать несколько тестов для него.

Добавление тестовых сценариев в сборку

Добавим несколько тестовых сценариев в файл distance.js. Для этого будем использовать Mocha, пакетный инструмент для написания тестов, и Chai в качестве нашей библиотеки установок. Выполните следующую команду:

$ yarn add --dev mocha chai

Затем создайте новый каталог app/test и новый файл app/test/distance.test.js, содержащий следующий фрагмент:

import {expect} from "chai"; import {distance, sortByDistance} from "../src/distance"; describe("distance", function() {  it("calculates distance with the good ol' Pythagorean Theorem", function() {    let origin = {x: 0.0, y: 0.0};    let point = {x: 3.0, y: 4.0};    expect(distance(point, origin)).to.equal(5);  }); }); describe("sortByDistance", function() {  it("sortsByDistance", function() {    let places = [      {name: "Far away", x: 100, y: 50},      {name: "Nearby", x: 20, y: 10},    ];    let origin = {name: "Origin", x: 0, y: 0};    let sorted = sortByDistance(origin, places);      expect(sorted[0].name).to.equal("Nearby");      expect(sorted[1].name).to.equal("Far away");    }); }); 

Теперь у вас есть тестовые сценарии для функций distance() и sortByDistance(), устанавливающие, что distance() вычисляет формулу расстояния, а sortByDistance() сортирует массивы координат с помощью формулы расстояния, используя наборы тестов Mocha и установки Chai. Довольно стандартная тестовая настройка.

Однако, если выполнить $ mocha app/test/distance.test.js, будет ошибка «Код JavaScript недопустим», потому что он содержит ключевое слово import, которое Node в данный момент не поддерживает. Но что если обойти это ограничение, используя Webpack для управления зависимостями тестового кода?

Прим.: это можно легко исправить, просто используя require вместо import в тестовых файлах. Но тестовый код также будет проходить через процесс сборки, если вы тестируете JavaScript-код типа Flow, который использует аннотации, или веб-приложения Vue.js, которые используют файлы .vue. Все они должны быть преобразованы в обычный JavaScript.

Список тестовых инструкций:

  1. Webpack строит граф зависимостей, начинающийся с тестовых файлов, а не с файлов приложения.
  2. Webpack создаёт файл JavaScript, содержащий весь тестовый код и его зависимости без ключевого слова import.
  3. Выполняются тесты, запуская Mocha для этого JavaScript-файла.

Всё это будет выглядеть следующим образом:

webpack na praktike s nulja do sozdanija avtotestov c68236f - Webpack на практике: с нуля до создания автотестов

Как можно увидеть, будут происходить две отдельные сборки. Одна из которых содержит код приложения в качестве точки входа и папку dist в качестве выходной директории, а другая — тестовые файлы в качестве точки входа и папку test-dist в качестве выходного каталога. Итак, давайте обновим конфигурационный файл, чтобы Webpack поддерживал вторую сборку:

let glob = require("glob"); let entry = __dirname + "/app/src/page.js"; let outputPath = __dirname + "/dist/"; if (process.env.TESTBUILD) {  entry = glob.sync(__dirname + "/app/test/**/*.test.js");  outputPath = __dirname + "/test-dist/"; } module.exports = {  entry: entry,  output: {    path: outputPath,  },  // остальная часть config-файла остаётся прежней 

Давайте разберёмся, что этот код делает. В строке 4 есть оператор if, который  выполняется, если системная переменная TESTBUILD имеет непустое значение. Так что, если вы выполните $ TESTBUILD=true webpack, то вам придётся вводить оператор if, но это не потребуется, если просто выполнить $ npx webpack.

Внутри оператора if происходит выбор, какой JS-файл является точкой входа. Вместо уже установленного выходного каталога dist будет папка test-dist. А вместо точки входа app/src/path.js — массив файлов, соответствующих глобальному выражению app/test/**/*.test.js. Другими словами, это все файлы, которые находятся в каталоге app/test и имеют путь, заканчивающийся на .test.js.

Новая точка входа и выходной путь передаются в объект module.exports, затем запускается Webpack для создания тестовой сборки. Конфигурация Webpack представляет собой обычный код JavaScript, поэтому можно использовать стандартную библиотеку Node и операторы if для её настройки. Если выполнить $ TESTBUILD=true npx webpack, то можно увидеть каталог test-dist. А если запустить $ npx mocha test-dist/main.js, то можно увидеть, как выполняются ваши тесты.

webpack na praktike s nulja do sozdanija avtotestov 3e95c04 - Webpack на практике: с нуля до создания автотестов

Наконец, в разделе scripts вашего package.json добавьте следующую строку:

"test": "TESTBUILD=true webpack && mocha test-dist/main.js && rm -rf test-dist"

Это означает, что когда вы выполняете $ yarn test, создаётся каталог test-dist (каталог сборки тестирования) с помощью Webpack, затем запускается Mocha для этой сборки, и, наконец, код $ rm -rf test-dist удаляет каталог test-dist, поскольку он больше не используется (Commit 7).

Маппинг исходных файлов тестового кода

Тестовая сборка готова, но есть один нюанс, касающийся тестирования кода. Если запустить Mocha для файла test-dist/main.js и один из этих тестов не пройдёт, как это будет выглядеть? Давайте сделаем тест формулы расстояния в app/test/distance.test.js ошибочным:

describe("distance", function() {  it("calculates distance with the good ol' Pythagorean Theorem", function() {    let origin = {x: 0.0, y: 0.0};    let point = {x: 3.0, y: 4.0};    expect(distance(point, origin)).to.equal(2071);  }); });

Выполните $ yarn test и вот что получится:

webpack na praktike s nulja do sozdanija avtotestov 02cde0c - Webpack на практике: с нуля до создания автотестов

Тест провален, но нельзя узнать, в какой строке исходного кода находится ошибка. Если таких тестов для веб-приложения у вас много, то поиски строки с ошибкой затянутся надолго.

Код с ошибкой находится в строке 8 в файле app/test/distance.test.js, но Mocha запускается для файла test-dist/main.js, поэтому, с точки зрения Mocha, ошибка находится в строке 116. К счастью, Webpack поддерживает source maps, которые покажут, какая строка кода соответствует ошибке. Source maps (или карты кода) — это файлы исходного кода, которые показывают точное соответствие элементов готового рабочего кода проекта и вашего кода разработки. Выполняется своего рода проход декодером по пакетному файлу main.js, чтобы получить исходные строки кода, которые соответствуют связному коду. Давайте обновим оператор if в файле webpack.config.js:

let entry = __dirname + "/app/src/path.js"; let outputPath = __dirname + "/dist/"; let devtool = ""; if (process.env.TESTBUILD) {  entry = glob.sync(__dirname + "/app/test/**/*.test.js");  outputPath = __dirname + "/test-dist/";  devtool = "source-map"; } 

Затем в объект module.exports добавим строку:

devtool: devtool,

Теперь в тестировочных сборках каталог test-dist будет содержать файл типа source maps. Выполните $ npx webpack TESTBUILD=true и каталог test-dist будет содержать файл main.js.map в дополнение к пакету main.js.

webpack na praktike s nulja do sozdanija avtotestov 9a68876 - Webpack на практике: с нуля до создания автотестов

Чтобы Mocha мог использовать эту source map при запуске тестов, необходимо установить ещё один пакет:

$ yarn add --dev source-map-support

Теперь, чтобы его использовать, нужно обновить скрипт Mocha в разделе scripts.test файла package.json:

TESTBUILD=true webpack && mocha test-dist/main.js --require source-map-support/register && rm -rf test-dist

Флаг --require source-map-support/register требует пакет source-map-support, это означает, что Mocha будет использовать source map, если она доступна. Теперь, если вы выполните $ yarn test и получите ошибку, вы увидите, в какой строке она находится, и сможете исправить код (Commit 8).

webpack na praktike s nulja do sozdanija avtotestov e00b8be - Webpack на практике: с нуля до создания автотестов

Итак, теперь у вас есть пример настройки обычных и тестовых сборок с использованием маппинга. Существует также множество других способов, которыми вы можете воспользоваться в своих сборках, например, объединение нескольких загрузчиков JavaScript для обработки вашего кода как на конвейере или же запуск Webpack как отладочного сервера, чтобы моментально видеть, как изменения в коде влияют на окончательную сборку Webpack. Продолжайте пробовать различные пакеты для компоновки файла webpack.config.js!

Для более точной настройки Webpack вам может быть полезен следующий материал:

Webpack 4: практические рекомендации по настройкеtproger.ru

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Honor Cup, бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Перейти к регистрации

  • 7 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close