Привет! Меня зовут Руслан. У меня за плечами 9 лет опыта в IT, из которых 5 я работаю на позиции Team Lead. Начинал как сервисный инженер, а сейчас руковожу командой разработки. За это время успешно запустил AR-приложение.
В NtechLab я занимаюсь развитием пользовательских интерфейсов для видеоаналитики и руковожу процессами разработки мобильного приложения на Flutter, которое помогает в поиске злоумышленников.
Основные задачи, которые можно решить с помощью этого подхода:
Разработка в условиях отсутствия бэкенда. Когда бэкенд еще не готов или находится в стадии активной разработки.
Изоляция от ошибок бэкенда. На ранних этапах разработки бэкенд может быть нестабилен и содержать множество багов.
Демонстрация и прототипирование. Моковые данные позволяют создавать демоверсии приложений, которые можно использовать для демонстрации заказчикам, инвесторам или другим заинтересованным сторонам.
Автоматизированное тестирование. Разрабатывая проект на моках изначально, не составит труда разбить проект на модули и написать юнит-тесты, используя созданные ранее моки.
Гибкость и контроль над данными. Моковые данные дают полный контроль над возвращаемыми значениями и сценариями. Это позволяет разработчикам моделировать различные состояния приложения и проверять, как приложение с ними справляется.
Тренировочная среда. Новым членам команды или стажерам можно предоставить окружение с моковыми данными для обучения и тренировки. Также большое интерпрайс-решение часто содержит множество микросервисов, и для локального разворачивания требуется достаточно много времени и глубокое понимание продукта.
Параллельная работа команд. Фронтенд- и бэкенд-команды могут работать параллельно и независимо друг от друга, что значительно ускоряет общий процесс разработки и уменьшает «узкие места» в проекте.
Пример использования в нашем проекте: как начать разработку frontend-проекта в сжатые сроки
Описание проблемы
Суть проблемы заключалась в том, что команда backend еще не завершила работу над проектом, а нам необходимо было начать активную разработку frontend. В результате приходилось работать одновременно с backend-командой, что усложняло процесс разработки. Мы могли только изучать документацию и получать данные через ограниченные интерфейсы.
Первоначальная идея состояла в том, чтобы развернуть неготовый сервис и начать разработку с ним. Однако это привело к множеству проблем:
Ограниченное количество готовых компонентов;
Проблемы с отладкой из-за недоработанного backend.
Выбор mock service worker (MSW) для эмуляции backend: быстрая и удобная разработка с Vite-плагином
В качестве альтернативы мы рассматривали написание минимального сервера на Node.js, но это заняло бы больше времени. Вместо этого мы выбрали использование MSW (Mock Service Worker), который позволил реализовать работу с HTTP API и WebSocket прямо внутри приложения.
Мы вынесли всю логику в отдельный Vite-плагин, который подключался к проекту только в режиме разработки и не включался в конечный бандл. Это позволило нам:
Быстро начать разработку frontend без ожидания готовности backend;
Удобно отлаживать код в изолированном окружении;
Уменьшить зависимость от изменений в backend и миграций.
Использование MSW предоставило нашей команде следующие преимущества:
Легкая интеграция с существующим процессом разработки;
Возможность эмулировать как HTTP API, так и WebSocket;
Отсутствие необходимости глубокого понимания backend для отладки;
Время на интеграцию и настройку MSW заняло всего несколько дней;
Стабильная работа frontend без зависимости от текущего состояния backend;
Безболезненная интеграция с минимальными проблемами.
Начало работы с MSW (Mock Service Worker)
Принципы создания моковых данных
Для создания моковых данных мы используем библиотеку с открытым исходным кодом MSW (Mock Service Worker). Эта библиотека перехватывает запросы и возвращает заранее определенные ответы, что делает ее идеальным инструментом для разработки и тестирования фронтенда. MSW предоставляет следующие возможности:
Определение сценариев для различных запросов и ответов;
Работа с REST- и GraphQL-запросами;
Поддержка динамических ответов для тестирования различных состояний приложения;
Легкая интеграция с тестовыми фреймворками, такими как Jest или Cypress.
Установка MSW
Чтобы установить библиотеку MSW, выполните следующую команду в терминале, находясь в папке вашего проекта:
npm install msw@latest --save-dev
Для работы с последней версией MSW требуется Node.js версии не ниже 18.0.0. Если ваш проект использует TypeScript, его версия должна быть не ниже 4.7.
Инициализация Mock Service Worker
После установки библиотеки необходимо инициализировать Mock Service Worker и создать необходимые файлы. Для этого выполните команду:
npx msw init ./public --save
Эта команда создаст файл mockServiceWorker.js
в директории ./public, где хранятся публичные статические файлы вашего проекта. Параметр --save сохранит путь до этой папки в package.json вашего проекта для будущих обновлений скрипта воркера.
Организация проекта и выбор инструмента
Создание файлов с моковыми ответами
Для хранения моковых ответов создайте файл ./mocks/api/_api_sessions.ts с содержимым, представленным ниже. Этот файл будет содержать стандартные ответы для различных статусных кодов.
Содержимое файла _api_sessions.ts
export const status200 = {
session_id: 'session-352561'
};
export const status400 = {
detail: [
{
msg: 'Error',
},
],
};
Шаги по подготовке и организации моковых данных
Организация структуры проекта: вынесите код LocalMockServer в папку ./mocks/api для удобства использования как в браузере, так и в тестах.
Создание файла с моковыми ответами: создайте файл ./mocks/api/_api_sessions.ts, который будет содержать стандартные моковые ответы для различных статусных кодов.
Добавление моковых ответов: в файле _api_sessions.ts определите объекты status200 и status400, которые будут возвращать соответствующие ответы на запросы. status200 содержит успешный ответ с session_id. status400 содержит ответ с ошибкой, включающий детальное сообщение.
Итоговая структура
После выполнения этих шагов структура вашей папки ./mocks/api должна выглядеть следующим образом:
/mocks
└── /api
└── _api_sessions.ts
Работа c MSWServer
MSWServer представляет собой класс, который обеспечивает гибкость и удобство при работе с моковыми данными в различных средах. Этот класс определяет основные методы и свойства, необходимые для инициализации и управления моковым сервером.
Регистрация mock URL для API-запросов: mockedResponses
Для упрощения работы с API-запросами содержимое ответов вынесено в отдельные файлы и зарегистрировано в общий объект mockedResponses. Это позволяет легко управлять стандартными моковыми ответами для каждой конечной точки и статусного кода.
const mockedResponses: MockedResponses = {
'@api/api/sessions': {
200: (await import('./api/_api_sessions.ts')).status200,
400: (await import('./api/_api_sessions.ts')).status400,
},
};
Функция init: регистрация мок-сервера в зависимости от платформы
Класс MSWServer предназначен для инициализации и управления мок-сервером, который эмулирует API-ответы, что особенно полезно для тестирования и разработки приложений. Мок-сервер позволяет разработчикам создавать и проверять поведение приложения без необходимости взаимодействия с реальными API-сервисами, что ускоряет процесс разработки и упрощает тестирование.
Конструктор класса MSWServer принимает функцию setup, которая отвечает за инициализацию мок-сервера. В зависимости от того, в какой среде выполняется код (браузер или Node.js), эта функция инициализирует либо setupWorker
для браузерной среды, либо setupServer
для среды Node.js. Таким образом, разработчики могут использовать один и тот же код для настройки мок-сервера в различных средах, что повышает гибкость и удобство использования.
Метод mock используется для регистрации мокового ответа для указанной конечной точки (endpoint) и статусного кода (status code). Этот метод создает обработчик, который перехватывает запросы, отправленные на указанную конечную точку, и возвращает заранее подготовленные моковые ответы. Это позволяет разработчикам эмулировать поведение реальных API-сервисов, что особенно полезно для тестирования и разработки приложений.
Параметры метода mock
1. method: string
- Описание: HTTP-метод, для которого необходимо зарегистрировать моковый ответ.
- Примеры значений: GET, POST, PUT, DELETE.
2. endpoint: string
- Описание: конечная точка API, для которой необходимо зарегистрировать моковый ответ.
- Примеры значений: @api/auth/login, @api/users/123, @api/products.
3. status: number
- Описание: статусный код HTTP-ответа, который должен возвращать моковый сервер.
- Примеры значений: 200 (успешный запрос), 400 (некорректный запрос), 404 (ресурс не найден).
4. callback: function (опционально)
- Описание: функция-обработчик, которая вызывается при получении запроса. Эта функция позволяет динамически формировать ответ в зависимости от параметров запроса.
- Пример использования: (response: any, params: any) => ({ token: '12345' }).
5. data: unknown (опционально)
- Описание: дополнительные данные, которые могут быть включены в ответ. Этот параметр опционален и по умолчанию равен пустому объекту.
- Пример значения: { message: 'Login successful' }.
Пример использования
server.mock('POST', '@api/products', 200, (response: any, params: any) => {
if (params.sort == 'true')
return Object.filter(response, (v: any) => v.favorite);
return response;
});
Пошаговая настройка мок-сервера
1. Инициализация мок-сервера:
- Создается новый экземпляр класса MSWServer с помощью функции setupWorker
. Эта функция отвечает за настройку мок-сервера в браузерной среде.
- const server = new MSWServer(setupWorker);
2. Регистрация мокового ответа:
- Вызывается метод mock для регистрации мокового ответа. В данном случае:
- Метод HTTP: POST
- Конечная точка: @api/auth/login
- Статусный код: 200
- Это означает, что все POST-запросы, отправленные на конечную точку @api/auth/login, будут перехватываться мок-сервером и возвращать успешный ответ с статусом 200.
- server.mock('POST', '@api/auth/login', 200);
3. Запуск мок-сервера:
- Вызывается метод start, который запускает мок-сервер. После запуска сервер начинает перехватывать и обрабатывать запросы согласно зарегистрированным моковым ответам.
- await server.start();
4. Монтирование приложения:
- После успешного запуска мок-сервера приложение монтируется на элемент с идентификатором #app. Это означает, что приложение начинает работать и можно тестировать его взаимодействие с мок-сервером.
- app.mount('#app');
Пример для создания класса:
import { MockSetupType, MSWServer } from './mocks/index.ts';
const server = new MSWServer();
server.init(MockSetupType.setupWorker).then(() => {
server.mock('POST', '@api/auth/login', 200);
server.start();
});
Функция setUrl: управление конечными точками API через псевдонимы
Для удобства работы с URL была добавлена отдельная функция setUrl, которая извлекает псевдоним из указанного URL и заменяет его на соответствующий alias, определенный в объекте aliases. Это позволяет гибко управлять конечными точками API. Функция принимает параметр endpoint — адресс API, для которой необходимо заменить псевдоним на соответствующий alias. Она извлекает часть URL до первого слеша (/), заменяет ее на соответствующий alias из объекта aliases и возвращает полный URL. Если псевдоним не найден, функция выбрасывает ошибку.
const setUrl = (endpoint: string) => {
const splitIdx = endpoint.indexOf('/');
const baseUrl = aliases[endpoint.substring(0, splitIdx)];
if (!baseUrl) {
throw new Error(Не найден псевдоним для конечной точки: [${baseUrl}]${endpoint}!);
}
return ${baseUrl}${endpoint.substring(splitIdx)};
};
Расширяем функционал с помощью Vite-плагина
Мы выбрали данный подход, чтобы максимально быстро добавлять новый функционал и проводить демонстрации. Вместо того чтобы каждый раз удалять или комментировать код при каждой сборке, мы решили воспользоваться более разумным способом — добавить отдельный плагин для работы в режиме разработки. Этот плагин позволяет легко включать и выключать мок-сервер, что упрощает процесс разработки и тестирования.
Ниже представлен код плагина vitePluginInitMockMSW
, который уже содержит инициализацию всех компонентов и настройки для работы в режиме разработки:
export function vitePluginInitMockMSW(): Plugin {
let config: ResolvedConfig;
return {
name: 'html-injection',
configResolved(resolvedConfig) {
// store the resolved config
config = resolvedConfig;
},
transformIndexHtml(html: string) {
let out = html;
if (config.mode == 'development') {
const data = '<script type="module" src="/__test__/setupPlugin.ts"></script>';
out = out.replace('</head>', `${data}\n</head>`);
}
return out;
},
};
}
Сохранение настроенной конфигурации:
Метод configResolved вызывается, когда Vite завершает разрешение конфигурации. В этом методе мы сохраняем настроенную конфигурацию в переменную config.
Изменение HTML в режиме разработки:
Метод transformIndexHtml вызывается для трансформации HTML перед тем, как он будет отправлен браузеру. Мы проверяем, находится ли приложение в режиме разработки
(config.mode === 'development')
.Если приложение находится в режиме разработки, мы добавляем скрипт для инициализации плагина в конец тега <head>. Этот скрипт подключает файл setupPlugin.ts, который содержит всю необходимую логику для инициализации мок-сервера.
Архитектурная структура решения
Полный код класса MSWServer
export class MSWServer {
setup: any = null;
constructor(setup: any) {
this.setup = setup();
}
printHandlers(): void {
console.debug("app envs", import.meta.env);
}
/**
* Мок: регистрация мокового ответа для указанной конечной точки и статусного кода.
* Это основная функция, используемая в тестах.
*/
mock(
method: string,
endpoint: string,
status: number,
callback?: object,
data = {}
) {
if (this.setup == null) {
return;
}
const url = setUrl(endpoint);
console.log(`${method.toLowerCase()} \\ ${url} \\ ${status}`);
const response = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
...mockedResponses[endpoint][status],
...data,
};
const handler = http[method.toLowerCase()](url, ({ params }) => {
const resData = callback != null ? callback(response, params) : response;
return HttpResponse.json(resData, { status: status });
});
const currentList = this.setup.listHandlers();
const inSelect = currentList.filter(
(listHandler: { info: { path: string } }) => listHandler.info.path === url
);
if (inSelect.length > 0) {
console.warn(`Handler for ${url} already exists!`);
}
this.setup.use(handler);
}
async start() {
if (this.setup != null) {
return await this.setup.start({
onUnhandledRequest(request, print) {
if (!request.url.startsWith("/api")) {
return;
}
print.warning();
},
});
}
return "not init";
}
mockWs(endpoint: string, sendData: object | number = -1) {
if (this.setup == null) {
return;
}
const url = setUrl(endpoint);
const wsLink = ws.link(url);
console.log(`ws \\ ${url} \\ init`);
const handler = wsLink.on("connection", ({ client }) => {
client.addEventListener("message", (event) => {
console.log("client sent:", event.data);
if (sendData != null) {
const resData = sendData != -1 ? sendData(event.data, client) : response;
switch (typeof resData) {
case "string":
client.send(JSON.stringify(resData));
break;
case "object":
client.send(JSON.stringify(resData));
break;
case null:
client.send("");
break;
default:
client.send(resData);
}
}
});
});
this.setup.use(handler);
}
}
Вывод
Использование MSW (Mock Service Worker) и MSWServer для эмуляции backend в процессе разработки оказало значительное положительное влияние на эффективность нашей команды. Этот подход позволил нам обеспечить:
Быстрый старт разработки: мы смогли начать разработку frontend незамедлительно, не дожидаясь завершения работы над backend;
Стабильность и контроль: мы избежали проблем, связанных с нестабильностью и багами раннего этапа разработки backend, обеспечив стабильную работу frontend;
Гибкость и удобство: благодаря использованию моковых данных, мы получили полный контроль над возвращаемыми значениями и сценариями, что упростило моделирование различных состояний приложения и ускорило процесс отладки;
Ускорение разработки: работа frontend- и backend-команд параллельно и независимо друг от друга уменьшила «узкие места» и значительно ускорила общий процесс разработки;
Эффективное тестирование: возможность легко интегрировать MSW с тестовыми фреймворками, такими как Jest или Cypress, позволила нам быстро настроить автоматизированное тестирование.
Организация моковых данных с помощью MSW и Vite-плагина также продемонстрировала свою эффективность, обеспечив удобное и изолированное окружение для разработки. Это позволило нашей команде сфокусироваться на задачах, не отвлекаясь на проблемы с backend, и обеспечить стабильность фронтенда.
В будущем мы планируем продолжать использовать LocalMockServer и MSW для новых проектов, совершенствуя процесс разработки и поддерживая высокую гибкость и контроль над данными. Эти инструменты доказали свою ценность и стали неотъемлемой частью нашего рабочего процесса.
kosuha666
А я использую mockoon программу, она проксирует бэкэнд или может просто быть бэкэндом которого еще нет. тоже удобно