WebSocket представляет собой протокол двунаправленной связи между клиентом и сервером поверх TCP. В отличие от HTTP-запросов, которые являются одноразовыми, WebSocket соединение устанавливается один раз и поддерживается открытым, что позволяет серверу отправлять данные клиенту в любой момент. Это особенно полезно для реального времени: чатов, уведомлений, стриминговых сервисов и многопользовательских приложений.
Cypress изначально ориентирован на тестирование фронтенда через HTTP-интерфейсы и DOM, но взаимодействие с WebSocket также возможно. Основная сложность заключается в асинхронной природе WebSocket и отсутствии встроенного API Cypress для полноценного управления соединениями. Решения обычно основаны на использовании сторонних библиотек и специальных хуков.
Для подключения к WebSocket в Cypress чаще всего применяются
JavaScript-библиотеки, такие как ws или встроенные
браузерные API WebSocket. В Node.js-контексте для тестов на
уровне сервера удобно использовать ws, а для браузерных
тестов — стандартный объект WebSocket.
Пример установки:
npm install ws --save-dev
Для браузерного тестирования отдельной установки не требуется, так
как WebSocket поддерживается большинством современных
браузеров.
Использование WebSocket в тестах Cypress требует управления асинхронными событиями: открытие соединения, получение сообщений и обработка ошибок.
Пример простого подключения:
describe('WebSocket тестирование', () => {
it('Подключение и получение сообщений', () => {
cy.window().then((win) => {
const socket = new win.WebSocket('ws://localhost:8080');
socket.addEventListener('open', () => {
console.log('Соединение установлено');
});
socket.addEventListener('message', (event) => {
console.log('Получено сообщение:', event.data);
});
socket.addEventListener('close', () => {
console.log('Соединение закрыто');
});
socket.addEventListener('error', (err) => {
console.error('Ошибка WebSocket:', err);
});
// Пример отправки сообщения
socket.addEventListener('open', () => {
socket.send(JSON.stringify({ type: 'ping' }));
});
});
});
});
Важный момент: все операции с WebSocket должны выполняться через
cy.window() или кастомные Cypress-команды, чтобы
сохранялась синхронизация с тестовым раннером. Прямое использование
Node.js-версии WebSocket может применяться для тестов на уровне сервера
или интеграционных тестов.
Cypress управляет тестами синхронно, но WebSocket работает
асинхронно. Чтобы интегрировать WebSocket с Cypress, используют
Cypress.Promise или кастомные команды, которые оборачивают
асинхронные события.
Пример ожидания конкретного сообщения:
Cypress.Commands.add('waitForWebSocketMessage', (socket, expectedType) => {
return new Cypress.Promise((resolve) => {
socket.addEventListener('message', function handler(event) {
const data = JSON.parse(event.data);
if (data.type === expectedType) {
socket.removeEventListener('message', handler);
resolve(data);
}
});
});
});
describe('WebSocket ожидание события', () => {
it('Должен дождаться сообщения типа "update"', () => {
cy.window().then((win) => {
const socket = new win.WebSocket('ws://localhost:8080');
socket.addEventListener('open', () => {
socket.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));
});
cy.waitForWebSocketMessage(socket, 'update').then((message) => {
expect(message).to.have.property('payload');
});
});
});
});
Этот подход позволяет синхронизировать события WebSocket с Cypress и использовать стандартные проверки и ассерт-методы.
error и корректно завершать
соединение.cy.wait, Cypress.Promise) для ожидания
сообщений, чтобы тесты не зависали навсегда.Для тестирования WebSocket на уровне сервера используется библиотека
ws:
const WebSocket = require('ws');
describe('Серверный WebSocket тест', () => {
let server;
before(() => {
server = new WebSocket.Server({ port: 8080 });
server.on('connection', (ws) => {
ws.on('message', (message) => {
const data = JSON.parse(message);
if (data.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }));
}
});
});
});
after(() => {
server.close();
});
it('Должен ответить на ping', (done) => {
const ws = new WebSocket('ws://localhost:8080');
ws.on('open', () => {
ws.send(JSON.stringify({ type: 'ping' }));
});
ws.on('message', (message) => {
const data = JSON.parse(message);
if (data.type === 'pong') {
done();
}
});
});
});
Такой подход позволяет тестировать серверные обработчики сообщений и их взаимодействие с клиентом в контролируемой среде.
Для упрощения повторного кода создаются кастомные команды, которые инкапсулируют подключение и отправку/ожидание сообщений:
Cypress.Commands.add('connectWebSocket', (url) => {
return cy.window().then((win) => {
const socket = new win.WebSocket(url);
return new Cypress.Promise((resolve) => {
socket.addEventListener('open', () => resolve(socket));
});
});
});
Cypress.Commands.add('sendWebSocketMessage', (socket, message) => {
socket.send(JSON.stringify(message));
});
Использование команд делает тесты компактными и читабельными:
cy.connectWebSocket('ws://localhost:8080').then((socket) => {
cy.sendWebSocketMessage(socket, { type: 'ping' });
});
WebSocket-тестирование часто требует:
Для этого создаются вспомогательные функции, которые ведут очередь сообщений и фильтруют события по типу, позволяя тестам оставаться предсказуемыми и устойчивыми к асинхронным задержкам.
Эта структура обеспечивает полное понимание того, как подключаться к WebSocket в Cypress, управлять событиями, синхронизировать асинхронные процессы и создавать повторяемые и надежные тесты как на клиентской, так и на серверной стороне.