Интеграционное тестирование в контексте STOMP.js применяется для проверки взаимодействия между клиентским кодом и брокером сообщений через WebSocket-протокол. В отличие от модульных тестов, где изолируются отдельные функции или классы, здесь проверяется полноценный поток сообщений: подключение, подписка, отправка, получение и обработка сообщений.
Основная цель — подтвердить корректность работы всей цепочки:
Ключевая особенность STOMP — текстовый протокол поверх WebSocket, что упрощает тестирование на уровне сообщений, но требует строгого контроля состояния соединения.
Интеграционные тесты STOMP.js невозможно реализовать без реального или эмулированного брокера сообщений. Обычно используется один из подходов:
Часто поднимается контейнеризированная среда:
Контейнеризация через Docker позволяет стабилизировать тестовую среду и исключить влияние локальных настроек.
Для JavaScript-стека часто создаётся минимальный сервер:
ws@stomp/stompjs server-side bridgeОн имитирует реальные сценарии обмена сообщениями и маршрутизации.
Типовой тест STOMP.js включает следующие этапы:
Каждый этап требует строгого контроля асинхронности, так как STOMP работает на событийной модели.
В интеграционных тестах чаще используется
@stomp/stompjs:
import { Client } from '@stomp/stompjs';
function createClient(url) {
return new Client({
brokerURL: url,
reconnectDelay: 0,
heartbeatIncoming: 0,
heartbeatOutgoing: 0,
debug: () => {}
});
}
Отключение heartbeat и reconnect важно для предсказуемости тестов.
Подключение является асинхронной операцией, поэтому тест должен учитывать события жизненного цикла:
function connectClient(client) {
return new Promise((resolve, reject) => {
client.onConn ect = () => resolve();
client.onStompEr ror = (frame) => reject(frame);
client.activate();
});
}
Контроль ошибок подключения критичен, поскольку сбой на этом этапе делает остальные проверки бессмысленными.
Подписка проверяет корректность маршрутизации сообщений через брокер:
function subscribe(client, destination) {
return new Promise((resolve) => {
client.subscribe(destination, (message) => {
resolve(message.body);
});
});
}
В интеграционных сценариях важно учитывать:
Отправка сообщений является ключевым элементом тестируемого потока:
function sendMessage(client, destination, payload) {
client.publish({
destination,
body: JSON.stringify(payload)
});
}
В интеграционных тестах проверяется не только факт отправки, но и то, что брокер правильно маршрутизирует сообщение.
Полный тест объединяет все стадии:
test('STOMP integration flow', async () => {
const client = createClient('ws://localhost:8080/ws');
await connectClient(client);
const messagePromise = subscribe(client, '/topic/events');
sendMessage(client, '/app/events', { type: 'PING' });
const response = await messagePromise;
expect(JSON.parse(response)).toEqual({ type: 'PONG' });
client.deactivate();
});
Важным аспектом является синхронизация подписки и отправки: подписка должна быть активна до публикации сообщения.
STOMP-сообщения не гарантируют мгновенную доставку. В интеграционных тестах применяются стратегии:
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
]);
}
При множественных событиях используется накопление:
function subscribeBuffer(client, destination, count) {
const messages = [];
return new Promise((resolve) => {
client.subscribe(destination, (msg) => {
messages.push(msg.body);
if (messages.length === count) {
resolve(messages);
}
});
});
}
Интеграционные тесты должны учитывать разделение каналов:
/app/* — входящие сообщения к серверу/topic/* — broadcast сообщения/queue/* — point-to-point доставкаКорректная маршрутизация проверяется через несколько клиентов:
const clientA = createClient(url);
const clientB = createClient(url);
await connectClient(clientA);
await connectClient(clientB);
const received = subscribe(clientB, '/topic/shared');
sendMessage(clientA, '/app/shared', { value: 1 });
Интеграционные сценарии часто включают проверку устойчивости:
client.onWebSocketCl ose = async () => {
await new Promise(r => setTimeout(r, 1000));
client.activate();
};
Проверяется:
При отсутствии реального брокера используется мок-сервер:
import { Server } from 'ws';
const wss = new Server({ port: 8081 });
wss.on('connection', (ws) => {
ws.on('message', (data) => {
ws.send(data.toString());
});
});
Этот подход ограничен, но подходит для проверки логики клиента без полноценного STOMP-брокера.
В Java-экосистеме часто применяется Spring Boot:
@EnableWebSocketMessageBrokerSimpMessagingTemplateИнтеграционные тесты JavaScript-клиента подключаются к Spring endpoint:
/ws WebSocket endpoint/topic broker relay/app message mappingЭто обеспечивает реалистичную проверку production-архитектуры.
При нестабильных тестах критично включать debug:
client = new Client({
debug: (msg) => console.log('[STOMP]', msg)
});
Анализируются:
Часто встречающиеся проблемы:
Особенно критична проблема повторного использования клиента между тестами без полной деактивации.
Для стабильности применяется:
/topic/test-${uuid})STOMP.js позволяет несколько подписок на один клиент:
client.subscribe('/topic/a', handlerA);
client.subscribe('/topic/b', handlerB);
В тестах важно проверять отсутствие перекрёстной доставки сообщений между каналами.
При увеличении количества сообщений тесты начинают выявлять:
Для анализа используется батч-отправка сообщений и измерение времени доставки.
STOMP работает с текстовыми payload, поэтому важна проверка:
Ошибки сериализации часто проявляются только на интеграционном уровне.
Интеграционные тесты STOMP.js обычно выполняются:
Типовая структура:
Ключевые принципы:
При флаппинг-тестах анализируется:
Используются временные маркеры и логирование frame-level событий STOMP протокола.