Вы когда-нибудь сталкивались с тем, что ваше приложение на JavaScript ломается, потому что API еще не готово? Или тесты тормозят, потому что каждый раз делают реальные запросы к серверу? Проблема в том, что вы не контролируете ответы. Вы ждете, пока бэкенд отдаст данные, а он молчит. Или, что еще хуже, возвращает случайные ошибки. В таких случаях помогает мокинг - искусственное имитирование API. И один из самых мощных инструментов для этого - Mock Service Worker (MSW).
Что такое MSW и почему он отличается от других решений
MSW - это библиотека, которая перехватывает HTTP-запросы на уровне браузера, прямо перед тем, как они уходят в сеть. Вместо того чтобы подменять функцию fetch или axios внутри кода, MSW работает как настоящий прокси. Он регистрирует Service Worker - специальный скрипт, который браузер запускает в фоне и который может перехватывать все сетевые запросы. Это значит, что ваше приложение не знает, что ответы - не настоящие. Оно видит те же заголовки, те же статусы, те же тела ответов, как будто сервер реально отвечает.
Почему это важно? Потому что другие библиотеки, которые просто заменяют fetch, работают только внутри кода. Если вы используете библиотеку, которая делает запросы через XMLHttpRequest, или если запрос идет из внешнего плагина - они могут обойти мок. MSW же ловит запросы на самом низком уровне - как будто вы включили настоящий сервер, только он отвечает заранее подготовленными данными.
Когда вы запускаете приложение с MSW, все запросы в DevTools выглядят точно так же, как будто они пошли на реальный API. Вы видите статус 200, заголовки, время ответа - всё как надо. Это делает отладку намного проще. Нет никакого "черного ящика". Вы точно знаете, что происходит.
Как настроить MSW в 5 минут
Установка MSW - это не сложный процесс. Сначала вы устанавливаете пакет через npm:
npm install msw
Затем вы генерируете файл-воркер - он называется mockServiceWorker.js. Его нужно положить в папку public (или static, в зависимости от фреймворка). Этот файл браузер будет загружать как обычный скрипт.
После этого вы создаете файл, например src/mock/browser.js, и в нем пишете простой код:
import { setupWorker, rest } from 'msw'
const worker = setupWorker(
rest.post('/login', (req, res, ctx) => {
const { email, password } = req.json()
if (email === '[email protected]' && password === '123') {
return res(
ctx.status(200),
ctx.json({ token: 'abc123', user: { id: 1, name: 'Иван' } })
)
}
return res(
ctx.status(403),
ctx.json({ error: 'Неверный логин или пароль' })
)
})
)
worker.start()
Этот код ловит POST-запрос на /login, проверяет email и пароль, и возвращает либо успех, либо ошибку. Вы не пишете никакой логики в компоненты. Вы не подменяете axios. Вы просто говорите браузеру: "Если кто-то запросит этот URL - отвечай вот так".
Важно: вызов worker.start() должен происходить до того, как ваше приложение начнет делать запросы. В React это делается в index.js или в main.jsx, до ReactDOM.render(). В Vue - в main.js, до createApp().mount().
Как мокать сложные сценарии
Простой логин - это хорошо. Но что, если вам нужно смоделировать загрузку списка товаров с пагинацией, фильтрацией и случайными ошибками? MSW справляется и с этим.
Вот пример, как можно мокать запрос к /api/posts?limit=5&userId=12:
rest.get('/api/posts', (req, res, ctx) => {
const { limit, userId } = req.url.searchParams
const limitNum = parseInt(limit) || 10
const userIdNum = parseInt(userId) || 1
const posts = Array.from({ length: limitNum }, (_, i) => ({
id: i + 1,
userId: userIdNum,
title: faker.lorem.sentence(),
body: faker.lorem.paragraphs(2)
}))
return res(ctx.json(posts))
})
Здесь используется библиотека faker.js, чтобы генерировать реалистичные данные: случайные заголовки, абзацы текста, идентификаторы. Это не статичный JSON - это динамические, живые данные, как будто они пришли из базы. Вы можете сделать так, чтобы при определенном userId возвращался пустой список, а при другом - ошибку 500. Или добавить задержку в 2 секунды, чтобы имитировать медленный интернет:
return res(
ctx.delay(2000),
ctx.json(posts)
)
Такой подход позволяет тестировать UI в разных условиях: медленные сети, пустые состояния, ошибки, загрузки. Вы не зависите от того, что бэкенд в данный момент доступен.
Можно ли мокать только часть запросов?
Да. Это одна из самых мощных фич MSW. Вы можете сказать: "Все запросы, кроме /api/auth, мокай. А к /api/auth пусть идут на реальный сервер". Это называется partial mocking.
Для этого вы просто не добавляете обработчик для /api/auth. MSW автоматически пропускает запросы, для которых нет мока. Это идеально, когда вы работаете над новой частью приложения, а старая часть уже работает с реальным API. Вы можете постепенно мокать эндпоинты, не ломая остальное.
Как использовать MSW в разных фреймворках
MSW не привязан к React, Vue или Angular. Он работает везде, где есть браузер и Service Worker. Это значит, что вы можете использовать его в:
- React - через
setupWorker()вindex.js - Vue 3 - в
main.js, перед монтированием приложения - Svelte - в
src/main.js - Astro - в
src/lib/mock.ts
Все настройки одинаковые. Нет нужды переучиваться под каждый фреймворк. Вы просто пишете обработчики и запускаете воркер. Это экономит время и снижает когнитивную нагрузку.
Как не мокать в продакшене
Один из самых важных моментов: вы не хотите, чтобы моки работали в продакшене. Это может привести к странному поведению: пользователь видит "предварительные данные", а реальный сервер отвечает чем-то другим.
Решение простое - вы используете переменную окружения. Например, в Vite вы можете проверить import.meta.env.MODE:
if (import.meta.env.MODE !== 'production') {
const { worker } = require('./mock/browser')
worker.start()
}
Или в Webpack - через process.env.NODE_ENV:
if (process.env.NODE_ENV === 'development') {
worker.start()
}
Таким образом, в продакшене MSW просто не загружается. Никакого влияния на производительность, никаких лишних файлов.
Ограничения и что нужно знать
MSW - мощный инструмент, но у него есть пара нюансов.
Первый - Service Worker не работает на localhost без HTTPS в некоторых браузерах. Особенно в старых версиях Chrome. Решение: используйте http://127.0.0.1 вместо http://localhost, или настройте локальный SSL-сертификат. В большинстве современных инструментов сборки (Vite, Next.js) это уже делается автоматически.
Второй - нужно правильно структурировать код. Не стоит писать все моки в одном файле. Лучше разделять их по логике: auth.mock.js, products.mock.js, users.mock.js. Импортировать их в один центральный файл, например mocks/index.js, а там уже передавать в setupWorker().
Третий - MSW не мокает запросы из Web Workers, Service Workers или iframe, если они не в той же области, что и основное приложение. Это редкий случай, но если вы работаете с вложенным контентом - стоит проверить.
Зачем это нужно? Пример из жизни
Представьте, что вы разрабатываете приложение для онлайн-магазина. У вас есть три команды: фронтенд, бэкенд, QA. Бэкенд еще не сделал эндпоинт /api/cart. Фронтенд не может тестировать корзину без него. Раньше они просто ждали. Теперь они просто мокают ответ:
- Пустая корзина - 200, []
- Корзина с товарами - 200, [{ id: 1, name: "Книга", price: 999 }]
- Ошибка 500 - для тестирования UI ошибки
QA-инженеры запускают тесты и видят, как приложение ведет себя при разных сценариях. Фронтенд-разработчики не ждут бэкенда. Бэкенд работает над API, не отвлекаясь на фронт. Всё идет параллельно. Это не миф - это стандартная практика в современных командах.
Что дальше?
MSW - это не панацея. Он не заменяет интеграционные тесты с реальным API. Но он делает разработку и тестирование быстрее, надежнее и предсказуемее. Когда вы перестаете зависеть от внешних систем, вы получаете контроль. И контроль - это свобода.
Начните с одного эндпоинта. Сделайте мок для авторизации. Потом добавьте мок для загрузки профиля. Потом - для списка товаров. Через неделю вы уже не будете вспоминать, как жили без этого.
Можно ли использовать MSW без Service Worker?
Нет. MSW работает именно через Service Worker - это его основа. Он не может работать в Node.js без браузерной среды, но для серверных тестов есть отдельная версия - msw/node, которая использует Node.js-заглушки. В браузере Service Worker обязателен.
Чем MSW отличается от jest-fetch-mock?
Jest-fetch-mock подменяет функцию fetch внутри тестов, но только в среде Jest. Он не работает в браузере, не ловит запросы из внешних библиотек и не отображается в DevTools. MSW работает в браузере, ловит все запросы, включая те, что идут через XMLHttpRequest, и показывает их как реальные в панели Network. Это делает его мощнее и реалистичнее.
Можно ли мокать POST-запросы с телом?
Да. MSW позволяет читать тело POST-запроса через req.json() или req.text(). Вы можете проверять содержимое, например: если в теле есть "status": "active", возвращать один ответ, если нет - другой. Это позволяет тестировать сложную логику на основе данных, которые отправляются клиентом.
Как мокать аутентификацию с токенами?
Вы можете сохранять токен в localStorage или sessionStorage при успешном логине. В последующих запросах проверяйте его наличие: если токен есть - возвращайте данные, если нет - 401. Это имитирует реальную аутентификацию без необходимости запускать настоящий сервер.
Поддерживает ли MSW WebSocket или SSE?
Нет. MSW работает только с HTTP-запросами: GET, POST, PUT, DELETE и т.д. Для WebSocket и Server-Sent Events есть другие инструменты. Но для большинства REST-API это не нужно - 95% запросов - это HTTP.